]>
Commit | Line | Data |
---|---|---|
09d64465 DM |
1 | Ext.define('Proxmox.node.Tasks', { |
2 | extend: 'Ext.grid.GridPanel', | |
3 | ||
9e059d56 DC |
4 | alias: 'widget.proxmoxNodeTasks', |
5 | ||
09d64465 | 6 | stateful: true, |
9e059d56 DC |
7 | stateId: 'pve-grid-node-tasks', |
8 | ||
09d64465 DM |
9 | loadMask: true, |
10 | sortableColumns: false, | |
09d64465 | 11 | |
6fc91fc6 TL |
12 | // set extra filter components, must have a 'name' property for the parameter, and must |
13 | // trigger a 'change' event if the value is 'undefined', it will not be sent to the api | |
9e059d56 | 14 | extraFilter: [], |
09d64465 | 15 | |
09d64465 | 16 | |
6fc91fc6 TL |
17 | // fixed filters which cannot be changed after instantiation, for example: |
18 | // { vmid: 100 } | |
9e059d56 DC |
19 | preFilter: {}, |
20 | ||
21 | controller: { | |
22 | xclass: 'Ext.app.ViewController', | |
23 | ||
24 | showTaskLog: function() { | |
25 | let me = this; | |
26 | let selection = me.getView().getSelection(); | |
27 | if (selection.length < 1) { | |
28 | return; | |
29 | } | |
09d64465 | 30 | |
9e059d56 DC |
31 | let rec = selection[0]; |
32 | ||
33 | Ext.create('Proxmox.window.TaskViewer', { | |
34 | upid: rec.data.upid, | |
35 | endtime: rec.data.endtime, | |
36 | }).show(); | |
37 | }, | |
38 | ||
39 | updateLayout: function() { | |
40 | let me = this; | |
6fc91fc6 TL |
41 | // update the scrollbar on every store load since the total count might be different. |
42 | // the buffered grid plugin does this only on (user) scrolling itself and even reduces | |
43 | // the scrollheight again when scrolling up | |
9e059d56 DC |
44 | me.getView().updateLayout(); |
45 | }, | |
09d64465 | 46 | |
9e059d56 DC |
47 | sinceChange: function(field, newval) { |
48 | let me = this; | |
49 | let vm = me.getViewModel(); | |
09d64465 | 50 | |
9e059d56 DC |
51 | vm.set('since', newval); |
52 | }, | |
09d64465 | 53 | |
9e059d56 DC |
54 | untilChange: function(field, newval, oldval) { |
55 | let me = this; | |
56 | let vm = me.getViewModel(); | |
57 | ||
58 | vm.set('until', newval); | |
59 | }, | |
60 | ||
61 | reload: function() { | |
62 | let me = this; | |
63 | let view = me.getView(); | |
64 | view.getStore().load(); | |
65 | }, | |
66 | ||
67 | showFilter: function(btn, pressed) { | |
68 | let me = this; | |
69 | let vm = me.getViewModel(); | |
70 | vm.set('showFilter', pressed); | |
71 | }, | |
72 | ||
73 | init: function(view) { | |
74 | let me = this; | |
75 | Proxmox.Utils.monStoreErrors(view, view.getStore(), true); | |
76 | }, | |
77 | }, | |
09d64465 | 78 | |
09d64465 | 79 | |
9e059d56 DC |
80 | listeners: { |
81 | itemdblclick: 'showTaskLog', | |
82 | }, | |
83 | ||
84 | viewModel: { | |
85 | data: { | |
86 | typefilter: '', | |
87 | statusfilter: '', | |
88 | datastore: '', | |
89 | showFilter: false, | |
90 | extraFilter: {}, | |
91 | since: null, | |
92 | until: null, | |
93 | }, | |
94 | ||
95 | formulas: { | |
96 | filterIcon: (get) => 'fa fa-filter' + (get('showFilter') ? ' info-blue' : ''), | |
97 | extraParams: function(get) { | |
98 | let me = this; | |
99 | let params = {}; | |
100 | if (get('typefilter')) { | |
101 | params.typefilter = get('typefilter'); | |
102 | } | |
103 | if (get('statusfilter')) { | |
104 | params.statusfilter = get('statusfilter'); | |
105 | } | |
106 | if (get('datastore')) { | |
107 | params.store = get('datastore'); | |
108 | } | |
109 | ||
110 | if (get('extraFilter')) { | |
111 | let extraFilter = get('extraFilter'); | |
112 | for (const [name, value] of Object.entries(extraFilter)) { | |
113 | if (value !== undefined && value !== null && value !== "") { | |
114 | params[name] = value; | |
b1d446d0 | 115 | } |
09d64465 | 116 | } |
9e059d56 DC |
117 | } |
118 | ||
119 | if (get('since')) { | |
120 | params.since = get('since').valueOf()/1000; | |
121 | } | |
122 | ||
123 | if (get('until')) { | |
124 | let until = new Date(get('until').getTime()); // copy object | |
125 | until.setDate(until.getDate() + 1); // end of the day | |
126 | params.until = until.valueOf()/1000; | |
127 | } | |
128 | ||
129 | me.getView().getStore().load(); | |
130 | ||
131 | return params; | |
132 | }, | |
133 | }, | |
134 | ||
135 | stores: { | |
136 | bufferedstore: { | |
137 | type: 'buffered', | |
138 | pageSize: 500, | |
139 | autoLoad: true, | |
140 | remoteFilter: true, | |
141 | model: 'proxmox-tasks', | |
142 | proxy: { | |
143 | type: 'proxmox', | |
144 | startParam: 'start', | |
145 | limitParam: 'limit', | |
146 | extraParams: '{extraParams}', | |
147 | url: "/api2/json/nodes/localhost/tasks", | |
148 | }, | |
149 | listeners: { | |
150 | prefetch: 'updateLayout', | |
122a2079 | 151 | }, |
09d64465 | 152 | }, |
9e059d56 DC |
153 | }, |
154 | }, | |
155 | ||
156 | bind: { | |
157 | store: '{bufferedstore}', | |
158 | }, | |
159 | ||
160 | dockedItems: [ | |
161 | { | |
162 | xtype: 'toolbar', | |
163 | items: [ | |
f138d11f | 164 | { |
9e059d56 DC |
165 | xtype: 'proxmoxButton', |
166 | text: gettext('View'), | |
167 | iconCls: 'fa fa-window-restore', | |
168 | disabled: true, | |
169 | handler: 'showTaskLog', | |
170 | }, | |
171 | { | |
172 | xtype: 'button', | |
173 | text: gettext('Reload'), | |
174 | iconCls: 'fa fa-refresh', | |
175 | handler: 'reload', | |
f138d11f TL |
176 | }, |
177 | '->', | |
09d64465 | 178 | { |
9e059d56 DC |
179 | xtype: 'button', |
180 | enableToggle: true, | |
181 | bind: { | |
182 | iconCls: '{filterIcon}', | |
183 | }, | |
184 | text: gettext('Filter'), | |
185 | stateful: true, | |
186 | stateId: 'task-showfilter', | |
187 | stateEvents: ['toggle'], | |
188 | applyState: function(state) { | |
189 | if (state.pressed !== undefined) { | |
190 | this.setPressed(state.pressed); | |
191 | } | |
192 | }, | |
193 | getState: function() { | |
194 | return { | |
195 | pressed: this.pressed, | |
196 | }; | |
122a2079 | 197 | }, |
09d64465 | 198 | listeners: { |
9e059d56 | 199 | toggle: 'showFilter', |
122a2079 | 200 | }, |
9e059d56 | 201 | }, |
09d64465 | 202 | ], |
9e059d56 DC |
203 | }, |
204 | { | |
205 | xtype: 'toolbar', | |
206 | dock: 'top', | |
207 | reference: 'filtertoolbar', | |
208 | layout: { | |
209 | type: 'hbox', | |
210 | align: 'top', | |
211 | }, | |
212 | bind: { | |
213 | hidden: '{!showFilter}', | |
214 | }, | |
215 | items: [ | |
09d64465 | 216 | { |
9e059d56 DC |
217 | xtype: 'container', |
218 | padding: 10, | |
219 | layout: { | |
220 | type: 'vbox', | |
221 | align: 'stretch', | |
122a2079 | 222 | }, |
9e059d56 DC |
223 | defaults: { |
224 | labelWidth: 80, | |
122a2079 | 225 | }, |
9e059d56 DC |
226 | // cannot bind the values directly, as it then changes also |
227 | // on blur, causing wrong reloads of the store | |
228 | items: [ | |
229 | { | |
230 | xtype: 'datefield', | |
231 | fieldLabel: gettext('Since'), | |
232 | format: 'Y-m-d', | |
233 | bind: { | |
234 | maxValue: '{until}', | |
235 | }, | |
236 | listeners: { | |
237 | change: 'sinceChange', | |
238 | }, | |
239 | }, | |
240 | { | |
241 | xtype: 'datefield', | |
242 | fieldLabel: gettext('Until'), | |
243 | format: 'Y-m-d', | |
244 | bind: { | |
245 | minValue: '{since}', | |
246 | }, | |
247 | listeners: { | |
248 | change: 'untilChange', | |
249 | }, | |
250 | }, | |
251 | ], | |
09d64465 | 252 | }, |
54dc3ab4 | 253 | { |
9e059d56 DC |
254 | xtype: 'container', |
255 | padding: 10, | |
256 | layout: { | |
257 | type: 'vbox', | |
258 | align: 'stretch', | |
54dc3ab4 | 259 | }, |
9e059d56 DC |
260 | defaults: { |
261 | labelWidth: 80, | |
262 | }, | |
263 | items: [ | |
264 | { | |
265 | xtype: 'pmxTaskTypeSelector', | |
266 | fieldLabel: gettext('Task Type'), | |
267 | emptyText: gettext('All'), | |
268 | bind: { | |
269 | value: '{typefilter}', | |
270 | }, | |
271 | }, | |
272 | { | |
273 | xtype: 'combobox', | |
274 | fieldLabel: gettext('Task Result'), | |
275 | emptyText: gettext('All'), | |
276 | multiSelect: true, | |
277 | store: [ | |
278 | ['ok', gettext('OK')], | |
279 | ['unknown', Proxmox.Utils.unknownText], | |
280 | ['warning', gettext('Warnings')], | |
281 | ['error', gettext('Errors')], | |
282 | ], | |
283 | bind: { | |
284 | value: '{statusfilter}', | |
285 | }, | |
286 | }, | |
287 | ], | |
54dc3ab4 | 288 | }, |
9e059d56 DC |
289 | ], |
290 | }, | |
291 | ], | |
292 | ||
293 | viewConfig: { | |
294 | trackOver: false, | |
295 | stripeRows: false, // does not work with getRowClass() | |
296 | emptyText: gettext('No Tasks found'), | |
297 | ||
298 | getRowClass: function(record, index) { | |
299 | let status = record.get('status'); | |
300 | ||
301 | if (status) { | |
302 | let parsed = Proxmox.Utils.parse_task_status(status); | |
303 | if (parsed === 'error') { | |
304 | return "proxmox-invalid-row"; | |
305 | } else if (parsed === 'warning') { | |
306 | return "proxmox-warning-row"; | |
307 | } | |
308 | } | |
309 | return ''; | |
310 | }, | |
311 | }, | |
312 | ||
313 | columns: [ | |
314 | { | |
315 | header: gettext("Start Time"), | |
316 | dataIndex: 'starttime', | |
317 | width: 130, | |
318 | renderer: function(value) { | |
319 | return Ext.Date.format(value, "M d H:i:s"); | |
320 | }, | |
321 | }, | |
322 | { | |
323 | header: gettext("End Time"), | |
324 | dataIndex: 'endtime', | |
325 | width: 130, | |
326 | renderer: function(value, metaData, record) { | |
327 | if (!value) { | |
328 | metaData.tdCls = "x-grid-row-loading"; | |
329 | return ''; | |
330 | } | |
331 | return Ext.Date.format(value, "M d H:i:s"); | |
332 | }, | |
333 | }, | |
334 | { | |
335 | header: gettext("Duration"), | |
336 | hidden: true, | |
337 | width: 80, | |
338 | renderer: function(value, metaData, record) { | |
339 | let start = record.data.starttime; | |
340 | if (start) { | |
341 | let end = record.data.endtime || Date.now(); | |
342 | let duration = end - start; | |
343 | if (duration > 0) { | |
344 | duration /= 1000; | |
345 | } | |
346 | return Proxmox.Utils.format_duration_human(duration); | |
347 | } | |
348 | return Proxmox.Utils.unknownText; | |
349 | }, | |
350 | }, | |
351 | { | |
352 | header: gettext("User name"), | |
353 | dataIndex: 'user', | |
354 | width: 150, | |
355 | }, | |
356 | { | |
357 | header: gettext("Description"), | |
358 | dataIndex: 'upid', | |
359 | flex: 1, | |
360 | renderer: Proxmox.Utils.render_upid, | |
361 | }, | |
362 | { | |
363 | header: gettext("Status"), | |
364 | dataIndex: 'status', | |
365 | width: 200, | |
366 | renderer: function(value, metaData, record) { | |
367 | if (value === undefined && !record.data.endtime) { | |
368 | metaData.tdCls = "x-grid-row-loading"; | |
369 | return ''; | |
370 | } | |
371 | ||
372 | let parsed = Proxmox.Utils.parse_task_status(value); | |
373 | switch (parsed) { | |
374 | case 'unknown': return Proxmox.Utils.unknownText; | |
375 | case 'error': return Proxmox.Utils.errorText + ': ' + value; | |
376 | case 'ok': // fall-through | |
377 | case 'warning': // fall-through | |
378 | default: return value; | |
379 | } | |
380 | }, | |
381 | }, | |
382 | ], | |
383 | ||
384 | initComponent: function() { | |
385 | const me = this; | |
386 | ||
387 | let updateExtraFilters = function(name, value) { | |
388 | let vm = me.getViewModel(); | |
389 | let extraFilter = Ext.clone(vm.get('extraFilter')); | |
390 | extraFilter[name] = value; | |
391 | vm.set('extraFilter', extraFilter); | |
392 | }; | |
393 | ||
394 | for (const [name, value] of Object.entries(me.preFilter)) { | |
395 | updateExtraFilters(name, value); | |
396 | } | |
397 | ||
398 | me.callParent(); | |
399 | ||
400 | let addFields = function(items) { | |
401 | me.lookup('filtertoolbar').add({ | |
402 | xtype: 'container', | |
403 | padding: 10, | |
404 | layout: { | |
405 | type: 'vbox', | |
406 | align: 'stretch', | |
09d64465 | 407 | }, |
9e059d56 DC |
408 | defaults: { |
409 | labelWidth: 80, | |
09d64465 | 410 | }, |
9e059d56 DC |
411 | items, |
412 | }); | |
413 | }; | |
b1d446d0 | 414 | |
9e059d56 DC |
415 | // start with a userfilter |
416 | me.extraFilter = [ | |
417 | { | |
418 | xtype: 'textfield', | |
419 | fieldLabel: gettext('User name'), | |
420 | changeOptions: { | |
421 | buffer: 500, | |
122a2079 | 422 | }, |
9e059d56 | 423 | name: 'userfilter', |
122a2079 | 424 | }, |
9e059d56 DC |
425 | ...me.extraFilter, |
426 | ]; | |
427 | let items = []; | |
428 | for (const filterTemplate of me.extraFilter) { | |
429 | let filter = Ext.clone(filterTemplate); | |
09d64465 | 430 | |
9e059d56 DC |
431 | filter.listeners = filter.listeners || {}; |
432 | filter.listeners.change = Ext.apply(filter.changeOptions || {}, { | |
433 | fn: function(field, value) { | |
434 | updateExtraFilters(filter.name, value); | |
435 | }, | |
436 | }); | |
437 | ||
438 | items.push(filter); | |
439 | if (items.length === 2) { | |
440 | addFields(items); | |
441 | items = []; | |
442 | } | |
443 | } | |
444 | ||
445 | addFields(items); | |
122a2079 | 446 | }, |
09d64465 | 447 | }); |