Skip to content

Commit 49fe520

Browse files
committed
dcache-view (admin): add quota-info-view (read-only
Motivation: Allow quotas to be listed in dCacheView as part of the admin interface. Modfication: Add the view, consisting of a table with sorters and filters which merges user and group quota info. Add icon on the admin toolbar. Result: Read-only listing of quotas. Target: master Patch: https://rb.dcache.org/r/13137/ Depends-on: #13136 Acked-by: Marina
1 parent 9b6528d commit 49fe520

5 files changed

Lines changed: 405 additions & 2 deletions

File tree

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
<link rel="import"
2+
href="../../../../bower_components/polymer/polymer-element.html">
3+
<link rel="import"
4+
href="../../../../bower_components/vaadin-grid/vaadin-grid.html">
5+
<link rel="import"
6+
href="../../../../bower_components/vaadin-grid/vaadin-grid-sorter.html">
7+
<link rel="import"
8+
href="../../../../bower_components/vaadin-grid/vaadin-grid-filter.html">
9+
<link rel="import"
10+
href="../../../../bower_components/vaadin-grid/vaadin-grid-column-group.html">
11+
<link rel="import"
12+
href="../../../../bower_components/paper-toolbar/paper-toolbar.html">
13+
<link rel="import"
14+
href="../../../../bower_components/iron-ajax/iron-ajax.html">
15+
<link rel="import"
16+
href="../../../../elements/dv-elements/admin/dv-vaadin-theme.html">
17+
<link rel="import"
18+
href="../../../../elements/dv-elements/admin/dv-admin-mixins.html">
19+
20+
<dom-module id="quota-info-view">
21+
<template>
22+
<style is="custom-style">
23+
:host {
24+
display: block;
25+
height: 80vh;
26+
@apply(--layout-vertical);
27+
}
28+
29+
paper-toolbar {
30+
color: var(--dv-main-background);
31+
background: var(--dv-charcoal-grey);
32+
}
33+
34+
.tablediv {
35+
height: 100%;
36+
}
37+
38+
input {
39+
@apply(--layout-flex);
40+
height: 95%;
41+
width: 90%;
42+
padding-right: 5px;
43+
}
44+
45+
paper-toolbar#controls {
46+
background: var(--dv-main-background);
47+
color: var(--dv-charcoal-grey);
48+
}
49+
50+
paper-radio-button {
51+
--paper-radio-button-checked-color: var(--dv-charcoal-grey);
52+
--paper-radio-button-checked-ink-color: var(--dv-charcoal-grey);
53+
--paper-radio-button-unchecked-color: var(--dv-light-grey);
54+
--paper-radio-button-unchecked-ink-color: var(--dv-light-grey);
55+
font-size: 10pt;
56+
}
57+
58+
.column-centre {
59+
text-align: center
60+
}
61+
62+
.red {
63+
color: rgb(221, 44, 0);
64+
}
65+
66+
.type0 {
67+
background: var(--dv-green-900);
68+
color: var(--dv-main-background);
69+
width: 100%;
70+
}
71+
72+
.type1 {
73+
background: var(--dv-blue-900);
74+
color: var(--dv-main-background);
75+
width: 100%;
76+
}
77+
</style>
78+
79+
<iron-ajax
80+
id="AjaxGetUsers"
81+
method="GET"
82+
handle-as="json"
83+
on-response="_handleUsersResponse"
84+
on-error="_handleError">
85+
</iron-ajax>
86+
<iron-ajax
87+
id="AjaxGetGroups"
88+
method="GET"
89+
handle-as="json"
90+
on-response="_handleGroupsResponse"
91+
on-error="_handleError">
92+
</iron-ajax>
93+
<paper-toolbar id="title">
94+
<div slot="top" class="title"><strong>User & Group Quotas</strong></div>
95+
</paper-toolbar>
96+
<hr/>
97+
<paper-toolbar id="controls">
98+
<paper-radio-group slot="top" selected="{{display}}">
99+
<paper-radio-button name="suffixed">
100+
Suffixed
101+
</paper-radio-button>
102+
<paper-radio-button name="raw">
103+
Bytes
104+
</paper-radio-button>
105+
</paper-radio-group>
106+
</paper-toolbar>
107+
<hr/>
108+
<div class="tablediv">
109+
<vaadin-grid aria-label="Quota Info" id="quotainfo"
110+
items="[[quotas]]" multi-sort="true" size="100">
111+
<vaadin-grid-column-group>
112+
<template class="header">
113+
<vaadin-grid-sorter path="id">
114+
Id
115+
</vaadin-grid-sorter>
116+
</template>
117+
<vaadin-grid-column width="75px">
118+
<template class="header">
119+
<vaadin-grid-filter aria-label="Id"
120+
path="id"
121+
value="[[_filterId]]">
122+
<input slot="filter"
123+
placeholder="Id"
124+
value="{{_filterId::input}}"
125+
focus-target="">
126+
</vaadin-grid-filter>
127+
</template>
128+
<template>[[item.id]]</template>
129+
</vaadin-grid-column>
130+
</vaadin-grid-column-group>
131+
<vaadin-grid-column-group>
132+
<template class="header">
133+
<vaadin-grid-sorter path="type.value">
134+
Type
135+
</vaadin-grid-sorter>
136+
</template>
137+
<vaadin-grid-column width="75px">
138+
<template class="header">
139+
<vaadin-grid-filter aria-label="Type"
140+
path="type.name"
141+
value="[[_filterType]]">
142+
<input slot="filter"
143+
placeholder="Type"
144+
value="{{_filterType::input}}"
145+
focus-target="">
146+
</vaadin-grid-filter>
147+
</template>
148+
<template>
149+
<span class$="type[[item.type.value]]">
150+
[[item.type.name]]
151+
</span>
152+
</template>
153+
</vaadin-grid-column>
154+
</vaadin-grid-column-group>
155+
<vaadin-grid-column-group>
156+
<template class="header">
157+
<vaadin-grid-sorter path="custodial">
158+
Custodial
159+
</vaadin-grid-sorter>
160+
</template>
161+
<vaadin-grid-column width="75px">
162+
<template>
163+
<span class$="[[_computedColumnCss(item.custodialExceeded)]]">
164+
[[item.custodial]]
165+
</span>
166+
</template>
167+
</vaadin-grid-column>
168+
</vaadin-grid-column-group>
169+
<vaadin-grid-column-group>
170+
<template class="header">
171+
<vaadin-grid-sorter path="custodialLimit">
172+
Custodial Limit
173+
</vaadin-grid-sorter>
174+
</template>
175+
<vaadin-grid-column width="75px">
176+
<template>
177+
<span class$="[[_computedColumnCss(item.custodialExceeded)]]">
178+
[[item.custodialLimit]]
179+
</span>
180+
</template>
181+
</vaadin-grid-column>
182+
</vaadin-grid-column-group>
183+
<vaadin-grid-column-group>
184+
<template class="header">
185+
<vaadin-grid-sorter path="output">
186+
Output
187+
</vaadin-grid-sorter>
188+
</template>
189+
<vaadin-grid-column width="75px">
190+
<template>
191+
<span class$="[[_computedColumnCss(item.outputExceeded)]]">
192+
[[item.output]]
193+
</span>
194+
</template>
195+
</vaadin-grid-column>
196+
</vaadin-grid-column-group>
197+
<vaadin-grid-column-group>
198+
<template class="header">
199+
<vaadin-grid-sorter path="outputLimit">
200+
Output Limit
201+
</vaadin-grid-sorter>
202+
</template>
203+
<vaadin-grid-column width="75px">
204+
<template>
205+
<span class$="[[_computedColumnCss(item.outputExceeded)]]">
206+
[[item.outputLimit]]
207+
</span>
208+
</template>
209+
</vaadin-grid-column>
210+
</vaadin-grid-column-group>
211+
<vaadin-grid-column-group>
212+
<template class="header">
213+
<vaadin-grid-sorter path="replica">
214+
Replica
215+
</vaadin-grid-sorter>
216+
</template>
217+
<vaadin-grid-column width="75px">
218+
<template>
219+
<span class$="[[_computedColumnCss(item.replicaExceeded)]]">
220+
[[item.replica]]
221+
</span>
222+
</template>
223+
</vaadin-grid-column>
224+
</vaadin-grid-column-group>
225+
<vaadin-grid-column-group>
226+
<template class="header">
227+
<vaadin-grid-sorter path="replicaLimit">
228+
Replica Limit
229+
</vaadin-grid-sorter>
230+
</template>
231+
<vaadin-grid-column width="75px">
232+
<template>
233+
<span class$="[[_computedColumnCss(item.replicaExceeded)]]">
234+
[[item.replicaLimit]]
235+
</span>
236+
</template>
237+
</vaadin-grid-column>
238+
</vaadin-grid-column-group>
239+
</vaadin-grid>
240+
</div>
241+
</template>
242+
243+
<script>
244+
class QuotaInfoView extends
245+
DcacheViewMixins.AdminAutoRefresh(DcacheViewMixins.AdminBase(DcacheViewMixins.Commons(Polymer.Element))) {
246+
247+
static get is() {
248+
return 'quota-info-view';
249+
}
250+
251+
static get properties() {
252+
return {
253+
quotas: {
254+
type: Array,
255+
notify: true,
256+
value: []
257+
},
258+
users: {
259+
type: Array,
260+
value: []
261+
},
262+
groups: {
263+
type: Array,
264+
value: []
265+
},
266+
display: {
267+
type: String,
268+
value: 'raw',
269+
notify: true,
270+
observer: '_requestQuotaInfo',
271+
},
272+
273+
}
274+
}
275+
276+
_computedColumnCss(exceeded) {
277+
return exceeded ? 'column-centre red' : 'column-centre';
278+
}
279+
280+
connectedCallback() {
281+
super.connectedCallback();
282+
this.componentPath = '/admin/quota-info';
283+
this.refreshAndReset(this._requestQuotaInfo.bind(this), 60000);
284+
}
285+
286+
_convertSizes(quota) {
287+
let current = this.isNumber(quota.custodial);
288+
let limit = this.isNumber(quota.custodialLimit);
289+
290+
if (current && limit) {
291+
quota.custodialExceeded = quota.custodial >= quota.custodialLimit;
292+
} else {
293+
quota.custodialExceeded = false;
294+
}
295+
296+
quota.custodial = this._handleConversion(quota.custodial, current, false);
297+
quota.custodialLimit = this._handleConversion(quota.custodialLimit, limit, true);
298+
299+
current = this.isNumber(quota.output);
300+
limit = this.isNumber(quota.outputLimit);
301+
302+
if (current && limit) {
303+
quota.outputExceeded = quota.output >= quota.outputLimit;
304+
} else {
305+
quota.outputExceeded = false;
306+
}
307+
308+
quota.output = this._handleConversion(quota.output, current, false);
309+
quota.outputLimit = this._handleConversion(quota.outputLimit, limit, true);
310+
311+
current = this.isNumber(quota.replica);
312+
limit = this.isNumber(quota.replicaLimit);
313+
314+
if (current && limit) {
315+
quota.replicaExceeded = quota.replica >= quota.replicaLimit;
316+
} else {
317+
quota.replicaExceeded = false;
318+
}
319+
320+
quota.replica = this._handleConversion(quota.replica, current, false);
321+
quota.replicaLimit = this._handleConversion(quota.replicaLimit, limit, true);
322+
}
323+
324+
_handleConversion(value, isNumber, isLimit) {
325+
if (!isNumber) {
326+
return isLimit ? 'UNDEF' :
327+
(this.display === 'suffixed' ? '0 Bytes' : '0');
328+
}
329+
330+
if (this.display === 'suffixed') {
331+
return this.convertBytesToNearestBinaryPrefix(value);
332+
}
333+
334+
return value;
335+
}
336+
337+
_handleError(event) {
338+
this.handleError(event.detail.error.message);
339+
}
340+
341+
_handleUsersResponse(event) {
342+
const input = event.detail.response;
343+
input.forEach((quota) => {
344+
quota.type = {name: "USER", value: 0};
345+
this._convertSizes(quota);
346+
});
347+
this.users = input;
348+
this._mergeQuotas();
349+
}
350+
351+
_handleGroupsResponse(event) {
352+
const input = event.detail.response;
353+
input.forEach((quota) => {
354+
quota.type = {name: "GROUP", value: 1};
355+
this._convertSizes(quota);
356+
});
357+
this.groups = input;
358+
this._mergeQuotas();
359+
}
360+
361+
_mergeQuotas() {
362+
if (this.users != null && this.groups != null) {
363+
const all = [];
364+
this.users.forEach((quota) => all.push(quota));
365+
this.groups.forEach((quota) => all.push(quota));
366+
this.quotas = all;
367+
}
368+
}
369+
370+
_requestQuotaInfo() {
371+
this.users = null;
372+
this.groups = null;
373+
this._requestUserQuotaInfo();
374+
this._requestGroupQuotaInfo();
375+
}
376+
377+
_requestUserQuotaInfo() {
378+
this.$.AjaxGetUsers.url = this.getUrl('quota/user', null);
379+
this.$.AjaxGetUsers.headers = this.getHeaders();
380+
this.$.AjaxGetUsers.generateRequest();
381+
}
382+
383+
_requestGroupQuotaInfo() {
384+
this.$.AjaxGetGroups.url = this.getUrl('quota/group', null);
385+
this.$.AjaxGetGroups.headers = this.getHeaders();
386+
this.$.AjaxGetGroups.generateRequest();
387+
}
388+
}
389+
390+
window.customElements.define(QuotaInfoView.is, QuotaInfoView);
391+
</script>
392+
</dom-module>

0 commit comments

Comments
 (0)