]> git.proxmox.com Git - proxmox-backup.git/blame - www/tape/DriveStatus.js
tape: add drive activity to drive status api
[proxmox-backup.git] / www / tape / DriveStatus.js
CommitLineData
7b608503
DC
1Ext.define('PBS.TapeManagement.DriveStatus', {
2 extend: 'Ext.panel.Panel',
3 alias: 'widget.pbsDriveStatus',
4 mixins: ['Proxmox.Mixin.CBind'],
5
fd641b99
DM
6 tools: [PBS.Utils.get_help_tool("tape_backup")],
7
7b608503
DC
8 cbindData: function(config) {
9 let me = this;
10 me.setTitle(`${gettext('Drive')}: ${me.drive}`);
a4003d90 11 let baseurl = `/api2/json/tape/drive/${me.drive}/`;
7b608503 12 return {
a4003d90
DC
13 driveStatusUrl: `${baseurl}/status`,
14 cartridgeMemoryUrl: `${baseurl}/cartridge-memory`,
7b608503
DC
15 };
16 },
17
a4003d90
DC
18 layout: {
19 type: 'vbox',
20 align: 'stretch',
21 },
7b608503
DC
22
23 bodyPadding: 5,
24
41f1132e
DC
25 viewModel: {
26 data: {
27 online: false,
6f3714b9
DC
28 busy: true,
29 loaded: false,
41f1132e
DC
30 },
31 },
32
7b608503
DC
33 controller: {
34 xclass: 'Ext.app.ViewController',
35
69e5ba29
DC
36 reloadTapeStore: function() {
37 Ext.ComponentQuery.query('navigationtree')[0].reloadTapeStore();
38 },
39
7b608503
DC
40 reload: function() {
41 let me = this;
42 me.lookup('statusgrid').rstore.load();
43 },
41f1132e
DC
44
45 onLoad: function() {
46 let me = this;
47 let statusgrid = me.lookup('statusgrid');
48c4193f 48 let online = statusgrid.getObjectValue('file-number') !== undefined;
41f1132e
DC
49 let vm = me.getViewModel();
50 vm.set('online', online);
646fc7f0
DC
51 let title = online ? gettext('Status') : gettext('Status (No Tape loaded)');
52 statusgrid.setTitle(title);
69e5ba29 53 Ext.ComponentQuery.query('navigationtree')[0].reloadTapeStore();
41f1132e
DC
54 },
55
6f3714b9
DC
56 onStateLoad: function(store) {
57 let me = this;
58 let view = me.getView();
59 let vm = me.getViewModel();
60 let driveRecord = store.findRecord('name', view.drive, 0, false, true, true);
61 let busy = !!driveRecord.data.state;
62 vm.set('busy', busy);
63 let statusgrid = me.lookup('statusgrid');
64 if (!vm.get('loaded')) {
65 if (busy) {
66 // have to use a timeout so that the component can be rendered first
67 // otherwise the 'mask' call errors out
68 setTimeout(function() {
69 statusgrid.mask(gettext('Drive is busy'));
70 }, 10);
71 } else {
72 // have to use a timeout so that the component can be rendered first
73 // otherwise the 'mask' call errors out
74 setTimeout(function() {
75 statusgrid.unmask();
76 }, 10);
77 me.reload();
78 vm.set('loaded', true);
79 }
80 }
81 },
82
41f1132e
DC
83 labelMedia: function() {
84 let me = this;
85 Ext.create('PBS.TapeManagement.LabelMediaWindow', {
86 driveid: me.getView().drive,
69e5ba29
DC
87 apiCallDone: function(success) {
88 if (success) {
89 me.reloadTapeStore();
90 }
91 },
41f1132e
DC
92 }).show();
93 },
94
e29f456e 95 format: function() {
30a08095
DC
96 let me = this;
97 let view = me.getView();
98 let driveid = view.drive;
e29f456e 99 PBS.Utils.driveCommand(driveid, 'format-media', {
30a08095
DC
100 waitMsgTarget: view,
101 method: 'POST',
102 success: function(response) {
69e5ba29 103 me.reloadTapeStore();
30a08095
DC
104 Ext.create('Proxmox.window.TaskProgress', {
105 upid: response.result.data,
106 taskDone: function() {
107 me.reload();
108 },
109 }).show();
110 },
111 });
112 },
113
b83e136f 114 ejectMedia: function() {
41f1132e
DC
115 let me = this;
116 let view = me.getView();
117 let driveid = view.drive;
118 PBS.Utils.driveCommand(driveid, 'eject-media', {
119 waitMsgTarget: view,
120 method: 'POST',
121 success: function(response) {
69e5ba29 122 me.reloadTapeStore();
41f1132e
DC
123 Ext.create('Proxmox.window.TaskProgress', {
124 upid: response.result.data,
125 taskDone: function() {
126 me.reload();
127 },
128 }).show();
129 },
130 });
131 },
132
133 catalog: function() {
134 let me = this;
135 let view = me.getView();
136 let drive = view.drive;
137 PBS.Utils.driveCommand(drive, 'catalog', {
138 waitMsgTarget: view,
139 method: 'POST',
140 success: function(response) {
69e5ba29 141 me.reloadTapeStore();
41f1132e
DC
142 Ext.create('Proxmox.window.TaskViewer', {
143 upid: response.result.data,
144 taskDone: function() {
145 me.reload();
146 },
147 }).show();
148 },
149 });
150 },
151
d0a0bad9
DC
152 readLabel: function() {
153 let me = this;
154 let view = me.getView();
155 let drive = view.drive;
156
157 PBS.Utils.driveCommand(drive, 'read-label', {
158 waitMsgTarget: view,
69e5ba29
DC
159 success: function(response) {
160 me.reloadTapeStore();
161 PBS.Utils.showMediaLabelWindow(response);
162 },
d0a0bad9
DC
163 });
164 },
165
166 volumeStatistics: function() {
167 let me = this;
168 let view = me.getView();
169 let drive = view.drive;
170 PBS.Utils.driveCommand(drive, 'volume-statistics', {
171 waitMsgTarget: view,
69e5ba29
DC
172 success: function(response) {
173 me.reloadTapeStore();
174 PBS.Utils.showVolumeStatisticsWindow(response);
175 },
d0a0bad9
DC
176 });
177 },
178
9bcdade8
DM
179 cartridgeMemory: function() {
180 let me = this;
181 let view = me.getView();
182 let drive = view.drive;
183 PBS.Utils.driveCommand(drive, 'cartridge-memory', {
184 waitMsgTarget: me.getView(),
69e5ba29
DC
185 success: function(response) {
186 me.reloadTapeStore();
187 PBS.Utils.showCartridgeMemoryWindow(response);
188 },
9bcdade8
DM
189 });
190 },
191
41f1132e
DC
192 init: function(view) {
193 let me = this;
194 me.mon(me.lookup('statusgrid').getStore().rstore, 'load', 'onLoad');
fa29d7eb 195 let tapeStore = Ext.ComponentQuery.query('navigationtree')[0].tapeStore;
6f3714b9
DC
196 me.mon(tapeStore, 'load', 'onStateLoad');
197 if (tapeStore.isLoaded()) {
198 me.onStateLoad(tapeStore);
199 }
41f1132e 200 },
7b608503
DC
201 },
202
7b608503
DC
203 tbar: [
204 {
205 xtype: 'proxmoxButton',
206 handler: 'reload',
207 text: gettext('Reload'),
6f3714b9
DC
208 disabled: true,
209 bind: {
210 disabled: '{busy}',
211 },
7b608503 212 },
41f1132e
DC
213 '-',
214 {
215 text: gettext('Label Media'),
216 xtype: 'proxmoxButton',
217 handler: 'labelMedia',
218 iconCls: 'fa fa-barcode',
219 disabled: true,
220 bind: {
221 disabled: '{!online}',
222 },
223 },
224 {
225 text: gettext('Eject'),
226 xtype: 'proxmoxButton',
227 handler: 'ejectMedia',
228 iconCls: 'fa fa-eject',
229 disabled: true,
230 bind: {
231 disabled: '{!online}',
232 },
233 },
30a08095 234 {
e29f456e 235 text: gettext('Format'),
30a08095 236 xtype: 'proxmoxButton',
e29f456e 237 handler: 'format',
30a08095
DC
238 iconCls: 'fa fa-trash-o',
239 dangerous: true,
a2e30cd5 240 confirmMsg: gettext('Are you sure you want to format the inserted tape?'),
30a08095
DC
241 disabled: true,
242 bind: {
243 disabled: '{!online}',
244 },
245 },
41f1132e
DC
246 {
247 text: gettext('Catalog'),
248 xtype: 'proxmoxButton',
249 handler: 'catalog',
250 iconCls: 'fa fa-book',
251 disabled: true,
252 bind: {
253 disabled: '{!online}',
254 },
255 },
d0a0bad9
DC
256 {
257 text: gettext('Read Label'),
258 xtype: 'proxmoxButton',
259 handler: 'readLabel',
260 iconCls: 'fa fa-tag',
261 disabled: true,
262 bind: {
263 disabled: '{!online}',
264 },
265 },
266 {
9bcdade8 267 text: gettext('Volume Statistics'),
d0a0bad9
DC
268 xtype: 'proxmoxButton',
269 handler: 'volumeStatistics',
270 iconCls: 'fa fa-line-chart',
271 disabled: true,
272 bind: {
273 disabled: '{!online}',
274 },
275 },
9bcdade8
DM
276 {
277 text: gettext('Cartridge Memory'),
278 xtype: 'proxmoxButton',
279 iconCls: 'fa fa-hdd-o',
280 handler: 'cartridgeMemory',
281 disabled: true,
282 bind: {
283 disabled: '{!online}',
284 },
285 },
d0a0bad9 286
7b608503
DC
287 ],
288
289 items: [
290 {
291 xtype: 'container',
292 layout: {
293 type: 'hbox',
294 align: 'stretch',
295 },
296 defaults: {
297 padding: 5,
298 flex: 1,
299 },
300 items: [
301 {
302 xtype: 'pbsDriveInfoPanel',
303 cbind: {
304 drive: '{drive}',
305 },
306 },
307 {
308 xtype: 'pbsDriveStatusGrid',
309 reference: 'statusgrid',
310 cbind: {
311 url: '{driveStatusUrl}',
312 },
313 },
314 ],
315 },
316 ],
317});
318
319Ext.define('PBS.TapeManagement.DriveStatusGrid', {
320 extend: 'Proxmox.grid.ObjectGrid',
321 alias: 'widget.pbsDriveStatusGrid',
322
323 title: gettext('Status'),
324
325 rows: {
48c4193f
DM
326 'density': {
327 required: true,
328 header: gettext('Tape Density'),
329 },
7b608503
DC
330 'blocksize': {
331 required: true,
84314647 332 header: gettext('Block Size'),
7b608503
DC
333 renderer: function(value) {
334 if (!value) {
335 return gettext('Dynamic');
336 }
337 return `${gettext('Fixed')} - ${Proxmox.Utils.format_size(value)}`;
338 },
339 },
48c4193f 340 'write-protect': {
7b608503 341 required: true,
48c4193f
DM
342 header: gettext('Write Protect'),
343 defaultValue: false,
511e4f69 344 renderer: Proxmox.Utils.format_boolean,
7b608503 345 },
48c4193f
DM
346 'compression': {
347 required: true,
348 header: gettext('Compression'),
511e4f69 349 renderer: Proxmox.Utils.format_boolean,
48c4193f 350 },
1d6b1e02
DC
351 'drive-activity': {
352 header: gettext('Drive Activity'),
353 renderer: PBS.Utils.renderDriveActivity,
354 },
48c4193f 355 'file-number': {
511e4f69
DC
356 header: gettext('Tape Position'),
357 renderer: function(value, mD, r, rI, cI, store) {
358 let me = this;
359 let filenr = value;
360 let rec = store.getById('block-number');
361 if (rec) {
362 let blocknr = rec.data.value;
363 return `File ${filenr}, Block ${blocknr}`;
364 }
365 return `File ${filenr}`;
366 },
48c4193f
DM
367 },
368 'block-number': {
511e4f69 369 visible: false,
7b608503
DC
370 },
371 'manufactured': {
372 header: gettext('Tape Manufacture Date'),
373 renderer: function(value) {
374 if (value) {
b0156179 375 return Ext.Date.format(new Date(value*1000), "Y-m-d");
7b608503
DC
376 }
377 return "";
378 },
379 },
380 'bytes-read': {
381 header: gettext('Tape Read'),
382 renderer: Proxmox.Utils.format_size,
383 },
384 'bytes-written': {
385 header: gettext('Tape Written'),
386 renderer: Proxmox.Utils.format_size,
387 },
e0f68926
DC
388 'medium-passes': {
389 header: gettext('Tape Passes'),
390 },
391 'medium-wearout': {
392 header: gettext('Tape Wearout'),
ac4a1fb3
DM
393 renderer: function(value) {
394 if (value !== undefined) {
395 return (value*100).toFixed(2) + "%";
396 }
397 return value;
398 },
e0f68926 399 },
511e4f69
DC
400 'alert-flags': {
401 header: gettext('Alert Flags'),
402 },
7b608503
DC
403 },
404});
405
406Ext.define('PBS.TapeManagement.DriveInfoPanel', {
407 extend: 'Ext.panel.Panel',
408 alias: 'widget.pbsDriveInfoPanel',
409
410 title: gettext('Information'),
411
412 defaults: {
413 printBar: false,
414 padding: 5,
415 },
416 bodyPadding: 15,
417
418 viewModel: {
d810014e
DC
419 data: {
420 drive: {},
421 },
422
423 formulas: {
424 driveState: function(get) {
425 let drive = get('drive');
426 return PBS.Utils.renderDriveState(drive.state, {});
427 },
428 },
7b608503
DC
429 },
430
431 items: [
432 {
433 xtype: 'pmxInfoWidget',
434 title: gettext('Name'),
435 bind: {
436 data: {
d810014e 437 text: '{drive.name}',
7b608503
DC
438 },
439 },
440 },
441 {
442 xtype: 'pmxInfoWidget',
443 title: gettext('Vendor'),
444 bind: {
445 data: {
d810014e 446 text: '{drive.vendor}',
7b608503
DC
447 },
448 },
449 },
450 {
451 xtype: 'pmxInfoWidget',
452 title: gettext('Model'),
453 bind: {
454 data: {
d810014e 455 text: '{drive.model}',
7b608503
DC
456 },
457 },
458 },
459 {
460 xtype: 'pmxInfoWidget',
461 title: gettext('Serial'),
462 bind: {
463 data: {
d810014e 464 text: '{drive.serial}',
7b608503
DC
465 },
466 },
467 },
468 {
469 xtype: 'pmxInfoWidget',
470 title: gettext('Path'),
471 bind: {
472 data: {
d810014e
DC
473 text: '{drive.path}',
474 },
475 },
476 },
477 {
478 xtype: 'pmxInfoWidget',
84d32846 479 reference: 'statewidget',
d810014e
DC
480 title: gettext('State'),
481 bind: {
482 data: {
483 text: '{driveState}',
7b608503
DC
484 },
485 },
486 },
487 ],
488
84d32846
DC
489 clickState: function(e, t, eOpts) {
490 let me = this;
491 let vm = me.getViewModel();
492 let drive = vm.get('drive');
493 if (t.classList.contains('right-aligned')) {
494 let upid = drive.state;
495 if (!upid || !upid.startsWith("UPID")) {
496 return;
497 }
498
499 Ext.create('Proxmox.window.TaskViewer', {
500 autoShow: true,
501 upid,
502 });
503 }
504 },
505
d810014e 506 updateData: function(store) {
7b608503 507 let me = this;
d810014e
DC
508 if (!store) {
509 return;
510 }
511 let record = store.findRecord('name', me.drive, 0, false, true, true);
7b608503
DC
512 if (!record) {
513 return;
514 }
515
516 let vm = me.getViewModel();
d810014e 517 vm.set('drive', record.data);
825dfe7e
DC
518 vm.notify();
519 me.updatePointer();
520 },
521
522 updatePointer: function() {
523 let me = this;
84d32846
DC
524 let stateWidget = me.down('pmxInfoWidget[reference=statewidget]');
525 let stateEl = stateWidget.getEl();
825dfe7e
DC
526 if (!stateEl) {
527 setTimeout(function() {
528 me.updatePointer();
529 }, 100);
530 return;
531 }
532
533 let vm = me.getViewModel();
534 let drive = vm.get('drive');
535
536 if (drive.state) {
84d32846
DC
537 stateEl.addCls('info-pointer');
538 } else {
539 stateEl.removeCls('info-pointer');
540 }
825dfe7e 541 },
84d32846 542
825dfe7e
DC
543 listeners: {
544 afterrender: function() {
545 let me = this;
546 let stateWidget = me.down('pmxInfoWidget[reference=statewidget]');
547 let stateEl = stateWidget.getEl();
548 stateEl.on('click', me.clickState, me);
549 },
7b608503
DC
550 },
551
552 initComponent: function() {
553 let me = this;
554 if (!me.drive) {
555 throw "no drive given";
556 }
557
825dfe7e
DC
558 me.callParent();
559
fa29d7eb 560 let tapeStore = Ext.ComponentQuery.query('navigationtree')[0].tapeStore;
d810014e
DC
561 me.mon(tapeStore, 'load', me.updateData, me);
562 if (tapeStore.isLoaded()) {
563 me.updateData(tapeStore);
7b608503 564 }
7b608503
DC
565 },
566});