Merge pull request #2282 from westlywright/progress-bar

Alert Users of Remaining Project Resource Quotas
This commit is contained in:
Westly Wright 2018-09-17 16:58:28 -07:00 committed by GitHub
commit 59ce6f88a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 134 additions and 38 deletions

View File

@ -39,6 +39,8 @@
editing=(or editing isNew) editing=(or editing isNew)
expanded=expanded expanded=expanded
limit=primaryResource.resourceQuota.limit limit=primaryResource.resourceQuota.limit
projectLimit=primaryResource.project.resourceQuota.limit
usedLimit=primaryResource.project.resourceQuota.usedLimit
nsDefaultQuota=primaryResource.project.namespaceDefaultResourceQuota.limit nsDefaultQuota=primaryResource.project.namespaceDefaultResourceQuota.limit
changed=(action "updateNsQuota") changed=(action "updateNsQuota")
}} }}

View File

@ -1,6 +1,13 @@
<td class="pr-10"> <td class="pr-10">
{{resource-quota-select editing=false quota=quota}} {{resource-quota-select editing=false quota=quota}}
</td> </td>
<td class="pr-10">
{{progress-bar-multi
values=quota.currentProjectUse
tooltipValues=quota.totalLimits
max=quota.max
}}
</td>
<td class="pr-10"> <td class="pr-10">
{{input-resource-quota editing=editing quota=quota key='value'}} {{input-resource-quota editing=editing quota=quota key='value'}}
</td> </td>

View File

@ -4,11 +4,15 @@ import Component from '@ember/component';
import { convertToMillis } from 'shared/utils/util'; import { convertToMillis } from 'shared/utils/util';
import { parseSi } from 'shared/utils/parse-unit'; import { parseSi } from 'shared/utils/parse-unit';
import layout from './template'; import layout from './template';
import { inject as service } from '@ember/service';
export default Component.extend({ export default Component.extend({
layout, intl: service(),
layout,
limit: null, limit: null,
usedLimit: null,
projectlimit: null,
projectQuota: null, projectQuota: null,
nsDefaultQuota: null, nsDefaultQuota: null,
@ -51,21 +55,49 @@ export default Component.extend({
}); });
this.sendAction('changed', Object.keys(out).length ? out : null); this.sendAction('changed', Object.keys(out).length ? out : null);
this.updateLimits();
}), }),
updateLimits() {
( get(this, 'quotaArray') || [] ).forEach((quota) => {
if ( quota.key ) {
const value = parseInt(get(quota, 'value'), 10) || 0;
const usedValue = get(quota, 'currentProjectUse.firstObject.value');
const newUse = get(quota, 'currentProjectUse.lastObject');
const totalLimits = get(quota, 'totalLimits.firstObject');
const myNewUse = usedValue + value;
const translation = get(this, 'intl').t('formResourceQuota.table.resources.tooltip', {
usedValue,
newUse: myNewUse,
remaining: ( get(quota, 'max') - ( myNewUse ) ),
});
set(newUse, 'value', value);
set(totalLimits, 'value', translation);
}
});
},
initQuotaArray() { initQuotaArray() {
let limit = get(this, 'limit'); const limit = get(this, 'limit');
const nsDefaultQuota = get(this, 'nsDefaultQuota'); const nsDefaultQuota = get(this, 'nsDefaultQuota');
const array = []; const array = [];
const used = get(this, 'usedLimit');
const currentProjectLimit = get(this, 'projectLimit')
const intl = get(this, 'intl');
Object.keys(nsDefaultQuota).forEach((key) => { Object.keys(nsDefaultQuota).forEach((key) => {
if ( key !== 'type' && typeof nsDefaultQuota[key] === 'string') { if ( key !== 'type' && typeof nsDefaultQuota[key] === 'string') {
let value; let value, currentProjectUse, totalLimits;
let usedValue = '';
let max = '';
let newUse = null;
if ( limit && !limit[key] ) { if ( limit && !limit[key] ) {
array.push({ array.push({
key, key,
value: '', value: '',
currentProjectUse: [],
}); });
return; return;
@ -73,16 +105,60 @@ export default Component.extend({
value = limit && limit[key] ? limit[key] : nsDefaultQuota[key]; value = limit && limit[key] ? limit[key] : nsDefaultQuota[key];
if ( key === 'limitsCpu' || key === 'requestsCpu' ) { switch (key) {
value = convertToMillis(value); case 'limitsCpu':
} else if ( key === 'limitsMemory' || key === 'requestsMemory' ) { case 'requestsCpu':
value = parseSi(value, 1024) / 1048576; value = convertToMillis(value);
} else if ( key === 'requestsStorage' ) { usedValue = convertToMillis(get(used, key));
value = parseSi(value) / (1024 ** 3); max = convertToMillis(get(currentProjectLimit, key));
break;
case 'limitsMemory':
case 'requestsMemory':
value = parseSi(value, 1024) / 1048576;
usedValue = parseSi(get(used, key), 1024) / 1048576;
max = parseSi(get(currentProjectLimit, key), 1024) / 1048576;
break;
case 'requestsStorage':
value = parseSi(value) / (1024 ** 3);
usedValue = parseSi(get(used, key)) / (1024 ** 3);
max = parseSi(get(currentProjectLimit, key)) / (1024 ** 3);
break;
default:
break;
} }
newUse = usedValue + value;
currentProjectUse = [
{
// current use
color: 'bg-error',
label: key,
value: usedValue,
},
{
// only need the new value here because progress-multi-bar adds this to the previous
color: 'bg-warning',
label: key,
value,
}
];
totalLimits = [{
label: get(this, 'intl').t(`formResourceQuota.resources.${ key }`),
value: intl.t('formResourceQuota.table.resources.tooltip', {
usedValue,
newUse,
remaining: ( max - newUse ),
})
}];
array.push({ array.push({
currentProjectUse,
key, key,
max,
totalLimits,
value, value,
}); });
} }

View File

@ -3,12 +3,13 @@
<thead> <thead>
<tr> <tr>
<th>{{t 'formResourceQuota.table.type.label'}}</th> <th>{{t 'formResourceQuota.table.type.label'}}</th>
<th width="200px">{{t 'formResourceQuota.table.resources.label'}}</th>
<th>{{t 'formResourceQuota.table.value.label'}}</th> <th>{{t 'formResourceQuota.table.value.label'}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{#each quotaArray as |quota|}} {{#each quotaArray as |quota|}}
{{namespace-quota-row {{namespace-quota-row
quota=quota quota=quota
editing=editing editing=editing
}} }}

View File

@ -15,14 +15,13 @@
}} }}
{{#if (eq kind "row")}} {{#if (eq kind "row")}}
<tr class="main-row"> <tr class="main-row">
<td valign="middle" class="row-check" style="padding-top: 2px;"> <td valign="middle" class="row-check" style="padding-top: 2px;">
{{#if projectsWithoutNamespace.length}} {{#if (and projectsWithoutNamespace.length (not model.length))}}
&nbsp; &nbsp;
{{else}} {{else}}
{{check-box nodeId=ns.id}} {{check-box nodeId=ns.id}}
{{/if}} {{/if}}
</td>
</td>
<td> <td>
{{badge-state model=ns}} {{badge-state model=ns}}
</td> </td>

View File

@ -32,7 +32,6 @@
{{#copy-inline clipboardText=model.displayIp}}{{format-ip model.displayIp}}{{/copy-inline}} / {{#copy-inline clipboardText=model.displayIp}}{{format-ip model.displayIp}}{{/copy-inline}} /
{{/if}} {{/if}}
{{#if (and showNode model.node)}} {{#if (and showNode model.node)}}
{{log model.node}}
<a href="{{href-to "node" scope.currentCluster.id model.node.id}}">{{model.node.displayName}}</a> / <a href="{{href-to "node" scope.currentCluster.id model.node.id}}">{{model.node.displayName}}</a> /
{{/if}} {{/if}}
{{t 'generic.createdDate' date=(date-from-now model.created) htmlSafe=true}} {{t 'generic.createdDate' date=(date-from-now model.created) htmlSafe=true}}

View File

@ -1,4 +1,4 @@
import { defineProperty, computed, get } from '@ember/object'; import { defineProperty, computed, get, observer } from '@ember/object';
import Component from '@ember/component'; import Component from '@ember/component';
import layout from './template'; import layout from './template';
@ -28,19 +28,19 @@ export default Component.extend({
init() { init() {
this._super(...arguments); this._super(...arguments);
let colorKey = this.get('colorKey'); let colorKey = get(this, 'colorKey');
let labelKey = this.get('labelKey'); let labelKey = get(this, 'labelKey');
let valueKey = this.get('valueKey'); let valueKey = get(this, 'valueKey');
let valueDep = `values.@each.{${ colorKey },${ labelKey },${ valueKey }}`; let valueDep = `values.@each.{${ colorKey },${ labelKey },${ valueKey }}`;
defineProperty(this, 'pieces', computed('min', 'max', valueDep, () => { defineProperty(this, 'pieces', computed('min', 'max', valueDep, () => {
let min = this.get('min'); let min = get(this, 'min');
let max = this.get('max'); let max = get(this, 'max');
var out = []; var out = [];
(this.get('values') || []).forEach((obj) => { (get(this, 'values') || []).forEach((obj) => {
out.push({ out.push({
color: get(obj, colorKey), color: get(obj, colorKey),
label: get(obj, labelKey), label: get(obj, labelKey),
@ -56,7 +56,7 @@ export default Component.extend({
} }
let sum = 0; let sum = 0;
let minPercent = this.get('minPercent'); let minPercent = get(this, 'minPercent');
out.forEach((obj) => { out.forEach((obj) => {
let per = Math.max(minPercent, toPercent(obj.value, min, max)); let per = Math.max(minPercent, toPercent(obj.value, min, max));
@ -79,12 +79,12 @@ export default Component.extend({
valueDep = `tooltipValues.@each.{${ labelKey },${ valueKey }}`; valueDep = `tooltipValues.@each.{${ labelKey },${ valueKey }}`;
defineProperty(this, 'tooltipContent', computed(valueDep, () => { defineProperty(this, 'tooltipContent', computed(valueDep, () => {
let labelKey = this.get('labelKey'); let labelKey = get(this, 'labelKey');
let valueKey = this.get('valueKey'); let valueKey = get(this, 'valueKey');
var out = []; var out = [];
(this.get('tooltipValues') || []).forEach((obj) => { (get(this, 'tooltipValues') || []).forEach((obj) => {
out.push(`${ get(obj, labelKey) }: ${ get(obj, valueKey) }`); out.push(`${ get(obj, labelKey) }: ${ get(obj, valueKey) }`);
}); });
@ -95,8 +95,9 @@ export default Component.extend({
didInsertElement() { didInsertElement() {
this.zIndexDidChange(); this.zIndexDidChange();
}, },
zIndexDidChange: function() {
this.$().css('zIndex', this.get('zIndex') || 'inherit'); zIndexDidChange: observer('zIndex', function() {
}.observes('zIndex'), this.$().css('zIndex', get(this, 'zIndex') || 'inherit');
}),
}); });

View File

@ -1,4 +1,12 @@
{{#tooltip-element type="tooltip-basic" model=tooltipContent tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="progress-bar" inlineBlock=false}} {{#tooltip-element
type="tooltip-basic"
model=tooltipContent
tooltipTemplate='tooltip-static'
aria-describedby="tooltip-base"
tooltipFor="progress-bar"
inlineBlock=true
classNames="full-width"
}}
<div class="progress"> <div class="progress">
{{~#each pieces as |obj|~}} {{~#each pieces as |obj|~}}
<div class="progress-bar {{obj.color}}" style={{obj.css}}></div> <div class="progress-bar {{obj.color}}" style={{obj.css}}></div>

View File

@ -40,11 +40,11 @@
<td data-title="{{dt.scale}}" class="text-center"> <td data-title="{{dt.scale}}" class="text-center">
{{progress-bar-multi {{progress-bar-multi
classNames="mt-5" classNames="mt-5"
labelKey="state" labelKey="state"
valueKey="count" valueKey="count"
values=model.podStates.byColor values=model.podStates.byColor
tooltipValues=model.podStates.byName tooltipValues=model.podStates.byName
}} }}
<small class="text-muted"> <small class="text-muted">
{{model.displayScale}} {{model.displayScale}}

View File

@ -3379,6 +3379,9 @@ formResourceQuota:
placeholder: e.g. 10 placeholder: e.g. 10
milliCpuPlaceholder: e.g. 500 milliCpuPlaceholder: e.g. 500
memoryPlaceholder: e.g. 1Gi memoryPlaceholder: e.g. 1Gi
resources:
label: Project Resource Availability
tooltip: "Reserved - { usedValue }, This Namespace - { newUse }, Available - { remaining }"
projectLimit: projectLimit:
label: Project Limit label: Project Limit
placeholder: e.g. 50 placeholder: e.g. 50