]>
git.proxmox.com Git - proxmox-backup.git/blob - www/tape/ChangerStatus.js
1 Ext
.define('pbs-slot-model', {
2 extend
: 'Ext.data.Model',
3 fields
: ['entry-id', 'label-text', 'is-labeled', ' model', 'name', 'vendor', 'serial', 'state', 'status', 'pool',
6 calculate: function(data
) {
7 return data
.state
!== undefined;
11 idProperty
: 'entry-id',
14 Ext
.define('PBS.TapeManagement.ChangerStatus', {
15 extend
: 'Ext.panel.Panel',
16 alias
: 'widget.pbsChangerStatus',
24 changerSelected
: (get) => get('changer') !== '',
29 xclass
: 'Ext.app.ViewController',
31 changerChange: function(field
, value
) {
33 let view
= me
.getView();
34 let vm
= me
.getViewModel();
35 vm
.set('changer', value
);
41 importTape: function(view
, rI
, cI
, button
, el
, record
) {
43 let vm
= me
.getViewModel();
44 let from = record
.data
['entry-id'];
45 let changer
= encodeURIComponent(vm
.get('changer'));
46 Ext
.create('Proxmox.window.Edit', {
47 title
: gettext('Import'),
49 submitText
: gettext('OK'),
51 url
: `/api2/extjs/tape/changer/${changer}/transfer`,
54 xtype
: 'displayfield',
58 fieldLabel
: gettext('From Slot'),
61 xtype
: 'proxmoxintegerfield',
63 fieldLabel
: gettext('To Slot'),
74 slotTransfer: function(view
, rI
, cI
, button
, el
, record
) {
76 let vm
= me
.getViewModel();
77 let from = record
.data
['entry-id'];
78 let changer
= encodeURIComponent(vm
.get('changer'));
79 Ext
.create('Proxmox.window.Edit', {
80 title
: gettext('Transfer'),
82 submitText
: gettext('OK'),
84 url
: `/api2/extjs/tape/changer/${changer}/transfer`,
87 xtype
: 'displayfield',
91 fieldLabel
: gettext('From Slot'),
94 xtype
: 'proxmoxintegerfield',
96 fieldLabel
: gettext('To Slot'),
100 destroy: function() {
107 erase: function(view
, rI
, cI
, button
, el
, record
) {
109 let vm
= me
.getViewModel();
110 let label
= record
.data
['label-text'];
112 let changer
= vm
.get('changer');
113 Ext
.create('PBS.TapeManagement.EraseWindow', {
117 destroy: function() {
124 load: function(view
, rI
, cI
, button
, el
, record
) {
126 let vm
= me
.getViewModel();
127 let label
= record
.data
['label-text'];
129 let changer
= vm
.get('changer');
131 Ext
.create('Proxmox.window.Edit', {
134 submitText
: gettext('OK'),
135 title
: gettext('Load Media into Drive'),
136 url
: `/api2/extjs/tape/drive`,
138 submitUrl: function(url
, values
) {
139 let drive
= values
.drive
;
141 return `${url}/${encodeURIComponent(drive)}/load-media`;
145 xtype
: 'displayfield',
149 fieldLabel
: gettext('Media'),
152 xtype
: 'pbsDriveSelector',
153 fieldLabel
: gettext('Drive'),
159 destroy: function() {
166 unload
: async
function(view
, rI
, cI
, button
, el
, record
) {
168 let drive
= record
.data
.name
;
170 await PBS
.Async
.api2({
173 url
: `/api2/extjs/tape/drive/${encodeURIComponent(drive)}/unload`,
176 Ext
.Msg
.alert(gettext('Error'), error
);
181 driveCommand: function(driveid
, command
, callback
, params
, method
) {
183 let view
= me
.getView();
184 params
= params
|| {};
185 method
= method
|| 'GET';
186 Proxmox
.Utils
.API2Request({
187 url
: `/api2/extjs/tape/drive/${driveid}/${command}`,
192 success: function(response
) {
195 failure: function(response
) {
196 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
201 cartridgeMemory: function(view
, rI
, cI
, button
, el
, record
) {
203 let drive
= record
.data
.name
;
204 me
.driveCommand(drive
, 'cartridge-memory', function(response
) {
205 Ext
.create('Ext.window.Window', {
206 title
: gettext('Cartridge Memory'),
216 data
: response
.result
.data
,
225 text
: gettext('Name'),
230 text
: gettext('Value'),
241 cleanDrive: function(view
, rI
, cI
, button
, el
, record
) {
243 me
.driveCommand(record
.data
.name
, 'clean', function(response
) {
248 volumeStatistics: function(view
, rI
, cI
, button
, el
, record
) {
250 let drive
= record
.data
.name
;
251 me
.driveCommand(drive
, 'volume-statistics', function(response
) {
252 Ext
.create('Ext.window.Window', {
253 title
: gettext('Volume Statistics'),
263 data
: response
.result
.data
,
272 text
: gettext('Name'),
277 text
: gettext('Value'),
288 readLabel: function(view
, rI
, cI
, button
, el
, record
) {
290 let drive
= record
.data
.name
;
291 me
.driveCommand(drive
, 'read-label', function(response
) {
293 for (const [key
, val
] of Object
.entries(response
.result
.data
)) {
294 lines
.push(`${key}: ${val}`);
297 let txt
= lines
.join('<br>');
300 title
: gettext('Label Information'),
307 status: function(view
, rI
, cI
, button
, el
, record
) {
309 let drive
= record
.data
.name
;
310 me
.driveCommand(drive
, 'status', function(response
) {
312 for (const [key
, val
] of Object
.entries(response
.result
.data
)) {
313 lines
.push(`${key}: ${val}`);
316 let txt
= lines
.join('<br>');
319 title
: gettext('Status'),
326 reloadList: function() {
328 me
.lookup('changerselector').getStore().load();
331 barcodeLabel: function() {
333 let vm
= me
.getViewModel();
334 let changer
= vm
.get('changer');
335 if (changer
=== '') {
339 Ext
.create('Proxmox.window.Edit', {
340 title
: gettext('Barcode Label'),
341 showTaskViewer
: true,
343 url
: '/api2/extjs/tape/drive',
344 submitUrl: function(url
, values
) {
345 let drive
= values
.drive
;
347 return `${url}/${encodeURIComponent(drive)}/barcode-label-media`;
352 xtype
: 'pbsDriveSelector',
353 fieldLabel
: gettext('Drive'),
358 xtype
: 'pbsMediaPoolSelector',
359 fieldLabel
: gettext('Pool'),
368 inventory: function() {
370 let vm
= me
.getViewModel();
371 let changer
= vm
.get('changer');
372 if (changer
=== '') {
376 Ext
.create('Proxmox.window.Edit', {
377 title
: gettext('Inventory'),
378 showTaskViewer
: true,
380 url
: '/api2/extjs/tape/drive',
381 submitUrl: function(url
, values
) {
382 let drive
= values
.drive
;
384 return `${url}/${encodeURIComponent(drive)}/inventory`;
389 xtype
: 'pbsDriveSelector',
390 fieldLabel
: gettext('Drive'),
398 scheduleReload: function(time
) {
400 if (me
.reloadTimeout
=== undefined) {
401 me
.reloadTimeout
= setTimeout(function() {
409 if (me
.reloadTimeout
!== undefined) {
410 clearTimeout(me
.reloadTimeout
);
411 me
.reloadTimeout
= undefined;
413 me
.reload_full(true);
416 reload_no_cache: function() {
418 if (me
.reloadTimeout
!== undefined) {
419 clearTimeout(me
.reloadTimeout
);
420 me
.reloadTimeout
= undefined;
422 me
.reload_full(false);
425 reload_full
: async
function(use_cache
) {
427 let view
= me
.getView();
428 let vm
= me
.getViewModel();
429 let changer
= vm
.get('changer');
430 if (changer
=== '') {
436 Proxmox
.Utils
.setErrorMask(view
, true);
437 Proxmox
.Utils
.setErrorMask(me
.lookup('content'));
439 let status_fut
= PBS
.Async
.api2({
442 url
: `/api2/extjs/tape/changer/${encodeURIComponent(changer)}/status`,
447 let drives_fut
= PBS
.Async
.api2({
449 url
: `/api2/extjs/tape/drive?changer=${encodeURIComponent(changer)}`,
452 let tapes_fut
= PBS
.Async
.api2({
454 url
: '/api2/extjs/tape/media/list',
457 "update-status": false,
461 let [status
, drives
, tapes_list
] = await Promise
.all([status_fut
, drives_fut
, tapes_fut
]);
471 for (const tape
of tapes_list
.result
.data
) {
472 tapes
[tape
['label-text']] = {
475 status
: tape
.expired
? 'expired' : tape
.status
,
479 let drive_entries
= {};
481 for (const entry
of drives
.result
.data
) {
482 drive_entries
[entry
['changer-drivenum'] || 0] = entry
;
485 for (let entry
of status
.result
.data
) {
486 let type
= entry
['entry-kind'];
488 if (type
=== 'drive' && drive_entries
[entry
['entry-id']] !== undefined) {
489 entry
= Ext
.applyIf(entry
, drive_entries
[entry
['entry-id']]);
492 if (tapes
[entry
['label-text']] !== undefined) {
493 entry
['is-labeled'] = true;
494 entry
.pool
= tapes
[entry
['label-text']].pool
;
495 entry
.status
= tapes
[entry
['label-text']].status
;
497 entry
['is-labeled'] = false;
500 data
[type
].push(entry
);
503 // the stores are diffstores and are only refreshed
504 // on a 'load' event, which does not trigger on 'setData'
505 // so we have to fire them ourselves
507 me
.lookup('slots').getStore().rstore
.setData(data
.slot
);
508 me
.lookup('slots').getStore().rstore
.fireEvent('load', me
, [], true);
510 me
.lookup('import_export').getStore().rstore
.setData(data
['import-export']);
511 me
.lookup('import_export').getStore().rstore
.fireEvent('load', me
, [], true);
513 me
.lookup('drives').getStore().rstore
.setData(data
.drive
);
514 me
.lookup('drives').getStore().rstore
.fireEvent('load', me
, [], true);
517 Proxmox
.Utils
.setErrorMask(view
);
519 Proxmox
.Utils
.setErrorMask(me
.lookup('content'));
522 Proxmox
.Utils
.setErrorMask(view
);
524 Proxmox
.Utils
.setErrorMask(me
.lookup('content'), err
.toString());
527 me
.scheduleReload(5000);
530 renderIsLabeled: function(value
, mD
, record
) {
531 if (!record
.data
['label-text']) {
535 if (record
.data
['label-text'].startsWith("CLN")) {
540 return gettext('Not Labeled');
543 let status
= record
.data
.status
;
544 if (record
.data
.pool
) {
545 return `${status} (${record.data.pool})`;
550 renderState: function(value
, md
, record
) {
552 return gettext('Idle');
555 let icon
= '<i class="fa fa-spinner fa-pulse fa-fw"></i>';
557 if (value
.startsWith("UPID")) {
558 let upid
= Proxmox
.Utils
.parse_task_upid(value
);
559 md
.tdCls
= "pointer";
560 return `${icon} ${upid.desc}`;
563 return `${icon} ${value}`;
567 'grid[reference=drives]': {
568 cellclick: function(table
, td
, ci
, rec
, tr
, ri
, e
) {
569 if (e
.position
.column
.dataIndex
!== 'state') {
573 let upid
= rec
.data
.state
;
574 if (!upid
|| !upid
.startsWith("UPID")) {
578 Ext
.create('Proxmox.window.TaskViewer', {
593 fieldLabel
: gettext('Changer'),
594 xtype
: 'pbsChangerSelector',
595 reference
: 'changerselector',
598 change
: 'changerChange',
603 text
: gettext('Reload'),
604 xtype
: 'proxmoxButton',
605 handler
: 'reload_no_cache',
610 text
: gettext('Barcode Label'),
611 xtype
: 'proxmoxButton',
612 handler
: 'barcodeLabel',
613 iconCls
: 'fa fa-barcode',
615 disabled
: '{!changerSelected}',
619 text
: gettext('Inventory'),
620 xtype
: 'proxmoxButton',
621 handler
: 'inventory',
622 iconCls
: 'fa fa-book',
624 disabled
: '{!changerSelected}',
636 reference
: 'content',
645 title
: gettext('Slots'),
652 model
: 'pbs-slot-model',
659 dataIndex
: 'entry-id',
663 text
: gettext("Content"),
664 dataIndex
: 'label-text',
666 renderer
: (value
) => value
|| '',
669 text
: gettext('Inventory'),
670 dataIndex
: 'is-labeled',
671 renderer
: 'renderIsLabeled',
675 text
: gettext('Actions'),
676 xtype
: 'actioncolumn',
680 iconCls
: 'fa fa-rotate-90 fa-exchange',
681 handler
: 'slotTransfer',
682 tooltip
: gettext('Transfer'),
683 isDisabled
: (v
, r
, c
, i
, rec
) => !rec
.data
['label-text'],
686 iconCls
: 'fa fa-trash-o',
688 tooltip
: gettext('Erase'),
689 isDisabled
: (v
, r
, c
, i
, rec
) => !rec
.data
['label-text'],
692 iconCls
: 'fa fa-rotate-90 fa-upload',
694 tooltip
: gettext('Load'),
695 isDisabled
: (v
, r
, c
, i
, rec
) => !rec
.data
['label-text'],
711 title
: gettext('Drives'),
716 model
: 'pbs-slot-model',
723 dataIndex
: 'entry-id',
728 text
: gettext("Content"),
729 dataIndex
: 'label-text',
731 renderer
: (value
) => value
|| '',
734 text
: gettext('Inventory'),
735 dataIndex
: 'is-labeled',
736 renderer
: 'renderIsLabeled',
740 text
: gettext("Name"),
744 renderer
: Ext
.htmlEncode
,
747 text
: gettext('State'),
750 renderer
: 'renderState',
753 text
: gettext("Vendor"),
758 renderer
: Ext
.htmlEncode
,
761 text
: gettext("Model"),
766 renderer
: Ext
.htmlEncode
,
769 text
: gettext("Serial"),
774 renderer
: Ext
.htmlEncode
,
777 xtype
: 'actioncolumn',
778 text
: gettext('Actions'),
782 iconCls
: 'fa fa-rotate-270 fa-upload',
784 tooltip
: gettext('Unload'),
785 isDisabled
: (v
, r
, c
, i
, rec
) => !rec
.data
['label-text'] || rec
.data
['is-blocked'],
788 iconCls
: 'fa fa-hdd-o',
789 handler
: 'cartridgeMemory',
790 tooltip
: gettext('Cartridge Memory'),
791 isDisabled
: (v
, r
, c
, i
, rec
) => !rec
.data
['label-text'] || rec
.data
['is-blocked'],
794 iconCls
: 'fa fa-line-chart',
795 handler
: 'volumeStatistics',
796 tooltip
: gettext('Volume Statistics'),
797 isDisabled
: (v
, r
, c
, i
, rec
) => !rec
.data
['label-text'] || rec
.data
['is-blocked'],
800 iconCls
: 'fa fa-tag',
801 handler
: 'readLabel',
802 tooltip
: gettext('Read Label'),
803 isDisabled
: (v
, r
, c
, i
, rec
) => !rec
.data
['label-text'] || rec
.data
['is-blocked'],
806 iconCls
: 'fa fa-info-circle',
807 tooltip
: gettext('Status'),
809 isDisabled
: (v
, r
, c
, i
, rec
) => rec
.data
['is-blocked'],
812 iconCls
: 'fa fa-shower',
813 tooltip
: gettext('Clean Drive'),
814 handler
: 'cleanDrive',
815 isDisabled
: (v
, r
, c
, i
, rec
) => rec
.data
['is-blocked'],
823 reference
: 'import_export',
828 model
: 'pbs-slot-model',
832 title
: gettext('Import-Export Slots'),
836 dataIndex
: 'entry-id',
840 text
: gettext("Content"),
841 dataIndex
: 'label-text',
842 renderer
: (value
) => value
|| '',
846 text
: gettext('Inventory'),
847 dataIndex
: 'is-labeled',
848 renderer
: 'renderIsLabeled',
852 text
: gettext('Actions'),
853 xtype
: 'actioncolumn',
856 iconCls
: 'fa fa-rotate-270 fa-upload',
857 handler
: 'importTape',
858 tooltip
: gettext('Import'),
859 isDisabled
: (v
, r
, c
, i
, rec
) => !rec
.data
['label-text'],