]>
Commit | Line | Data |
---|---|---|
a83cedc2 DC |
1 | Ext.define('pbs-slot-model', { |
2 | extend: 'Ext.data.Model', | |
24e84128 | 3 | fields: ['entry-id', 'label-text', 'is-labeled', ' model', 'name', 'vendor', 'serial', 'state', 'status', 'pool', |
a83cedc2 DC |
4 | { |
5 | name: 'is-blocked', | |
6 | calculate: function(data) { | |
7 | return data.state !== undefined; | |
8 | }, | |
9 | }, | |
10 | ], | |
11 | idProperty: 'entry-id', | |
12 | }); | |
13 | ||
fd9aa8df DC |
14 | Ext.define('PBS.TapeManagement.ChangerStatus', { |
15 | extend: 'Ext.panel.Panel', | |
16 | alias: 'widget.pbsChangerStatus', | |
17 | ||
18 | viewModel: { | |
19 | data: { | |
20 | changer: '', | |
21 | }, | |
22 | ||
23 | formulas: { | |
24 | changerSelected: (get) => get('changer') !== '', | |
25 | }, | |
26 | }, | |
27 | ||
28 | controller: { | |
29 | xclass: 'Ext.app.ViewController', | |
30 | ||
31 | changerChange: function(field, value) { | |
32 | let me = this; | |
33 | let view = me.getView(); | |
34 | let vm = me.getViewModel(); | |
35 | vm.set('changer', value); | |
36 | if (view.rendered) { | |
37 | me.reload(); | |
38 | } | |
39 | }, | |
40 | ||
58791864 DC |
41 | importTape: function(view, rI, cI, button, el, record) { |
42 | let me = this; | |
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'), | |
48 | isCreate: true, | |
49 | submitText: gettext('OK'), | |
50 | method: 'POST', | |
51 | url: `/api2/extjs/tape/changer/${changer}/transfer`, | |
52 | items: [ | |
53 | { | |
54 | xtype: 'displayfield', | |
55 | name: 'from', | |
56 | value: from, | |
57 | submitValue: true, | |
58 | fieldLabel: gettext('From Slot'), | |
59 | }, | |
60 | { | |
61 | xtype: 'proxmoxintegerfield', | |
62 | name: 'to', | |
63 | fieldLabel: gettext('To Slot'), | |
64 | }, | |
65 | ], | |
66 | listeners: { | |
67 | destroy: function() { | |
68 | me.reload(); | |
69 | }, | |
70 | }, | |
71 | }).show(); | |
72 | }, | |
73 | ||
fd9aa8df DC |
74 | slotTransfer: function(view, rI, cI, button, el, record) { |
75 | let me = this; | |
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'), | |
81 | isCreate: true, | |
82 | submitText: gettext('OK'), | |
83 | method: 'POST', | |
84 | url: `/api2/extjs/tape/changer/${changer}/transfer`, | |
85 | items: [ | |
86 | { | |
87 | xtype: 'displayfield', | |
88 | name: 'from', | |
89 | value: from, | |
90 | submitValue: true, | |
91 | fieldLabel: gettext('From Slot'), | |
92 | }, | |
93 | { | |
94 | xtype: 'proxmoxintegerfield', | |
95 | name: 'to', | |
96 | fieldLabel: gettext('To Slot'), | |
97 | }, | |
98 | ], | |
99 | listeners: { | |
100 | destroy: function() { | |
101 | me.reload(); | |
102 | }, | |
103 | }, | |
104 | }).show(); | |
105 | }, | |
106 | ||
85205bc2 DC |
107 | erase: function(view, rI, cI, button, el, record) { |
108 | let me = this; | |
109 | let vm = me.getViewModel(); | |
110 | let label = record.data['label-text']; | |
111 | ||
112 | let changer = vm.get('changer'); | |
113 | Ext.create('PBS.TapeManagement.EraseWindow', { | |
114 | label, | |
115 | changer, | |
116 | listeners: { | |
117 | destroy: function() { | |
118 | me.reload(); | |
119 | }, | |
120 | }, | |
121 | }).show(); | |
122 | }, | |
123 | ||
fd9aa8df DC |
124 | load: function(view, rI, cI, button, el, record) { |
125 | let me = this; | |
126 | let vm = me.getViewModel(); | |
127 | let label = record.data['label-text']; | |
128 | ||
129 | let changer = vm.get('changer'); | |
130 | ||
131 | Ext.create('Proxmox.window.Edit', { | |
132 | isCreate: true, | |
918a3672 | 133 | autoShow: true, |
fd9aa8df DC |
134 | submitText: gettext('OK'), |
135 | title: gettext('Load Media into Drive'), | |
136 | url: `/api2/extjs/tape/drive`, | |
918a3672 | 137 | method: 'POST', |
fd9aa8df DC |
138 | submitUrl: function(url, values) { |
139 | let drive = values.drive; | |
140 | delete values.drive; | |
141 | return `${url}/${encodeURIComponent(drive)}/load-media`; | |
142 | }, | |
143 | items: [ | |
144 | { | |
145 | xtype: 'displayfield', | |
146 | name: 'label-text', | |
147 | value: label, | |
148 | submitValue: true, | |
149 | fieldLabel: gettext('Media'), | |
150 | }, | |
151 | { | |
152 | xtype: 'pbsDriveSelector', | |
153 | fieldLabel: gettext('Drive'), | |
154 | changer: changer, | |
155 | name: 'drive', | |
156 | }, | |
157 | ], | |
158 | listeners: { | |
159 | destroy: function() { | |
160 | me.reload(); | |
161 | }, | |
162 | }, | |
918a3672 | 163 | }); |
fd9aa8df DC |
164 | }, |
165 | ||
166 | unload: async function(view, rI, cI, button, el, record) { | |
167 | let me = this; | |
168 | let drive = record.data.name; | |
fd9aa8df | 169 | try { |
4f688e09 | 170 | await PBS.Async.api2({ |
918a3672 | 171 | method: 'POST', |
b0338178 | 172 | timeout: 5*60*1000, |
fd9aa8df DC |
173 | url: `/api2/extjs/tape/drive/${encodeURIComponent(drive)}/unload`, |
174 | }); | |
fd9aa8df DC |
175 | } catch (error) { |
176 | Ext.Msg.alert(gettext('Error'), error); | |
fd9aa8df | 177 | } |
4f688e09 | 178 | me.reload(); |
fd9aa8df DC |
179 | }, |
180 | ||
181 | driveCommand: function(driveid, command, callback, params, method) { | |
182 | let me = this; | |
183 | let view = me.getView(); | |
184 | params = params || {}; | |
185 | method = method || 'GET'; | |
186 | Proxmox.Utils.API2Request({ | |
187 | url: `/api2/extjs/tape/drive/${driveid}/${command}`, | |
b0338178 | 188 | timeout: 5*60*1000, |
fd9aa8df DC |
189 | method, |
190 | waitMsgTarget: view, | |
191 | params, | |
192 | success: function(response) { | |
193 | callback(response); | |
194 | }, | |
195 | failure: function(response) { | |
196 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
197 | }, | |
198 | }); | |
199 | }, | |
200 | ||
201 | cartridgeMemory: function(view, rI, cI, button, el, record) { | |
202 | let me = this; | |
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'), | |
207 | modal: true, | |
208 | width: 600, | |
209 | height: 450, | |
210 | layout: 'fit', | |
211 | scrollable: true, | |
212 | items: [ | |
213 | { | |
214 | xtype: 'grid', | |
215 | store: { | |
216 | data: response.result.data, | |
217 | }, | |
218 | columns: [ | |
219 | { | |
220 | text: gettext('ID'), | |
221 | dataIndex: 'id', | |
222 | width: 60, | |
223 | }, | |
224 | { | |
225 | text: gettext('Name'), | |
226 | dataIndex: 'name', | |
227 | flex: 2, | |
228 | }, | |
229 | { | |
230 | text: gettext('Value'), | |
231 | dataIndex: 'value', | |
232 | flex: 1, | |
233 | }, | |
234 | ], | |
235 | }, | |
236 | ], | |
237 | }).show(); | |
238 | }); | |
239 | }, | |
240 | ||
241 | cleanDrive: function(view, rI, cI, button, el, record) { | |
242 | let me = this; | |
60473d23 DC |
243 | me.driveCommand(record.data.name, 'clean', function(response) { |
244 | me.reload(); | |
fd9aa8df DC |
245 | }, {}, 'PUT'); |
246 | }, | |
247 | ||
248 | volumeStatistics: function(view, rI, cI, button, el, record) { | |
249 | let me = this; | |
250 | let drive = record.data.name; | |
251 | me.driveCommand(drive, 'volume-statistics', function(response) { | |
e1beaae4 DM |
252 | let list = []; |
253 | for (let [key, val] of Object.entries(response.result.data)) { | |
41685061 DM |
254 | if (key === 'total-native-capacity' || |
255 | key === 'total-used-native-capacity' || | |
256 | key === 'lifetime-bytes-read' || | |
257 | key === 'lifetime-bytes-written' || | |
258 | key === 'last-mount-bytes-read' || | |
259 | key === 'last-mount-bytes-written') | |
260 | { | |
261 | val = Proxmox.Utils.format_size(val); | |
262 | } | |
e1beaae4 DM |
263 | list.push({ key: key, value: val }); |
264 | } | |
fd9aa8df DC |
265 | Ext.create('Ext.window.Window', { |
266 | title: gettext('Volume Statistics'), | |
267 | modal: true, | |
268 | width: 600, | |
269 | height: 450, | |
270 | layout: 'fit', | |
271 | scrollable: true, | |
272 | items: [ | |
273 | { | |
274 | xtype: 'grid', | |
275 | store: { | |
e1beaae4 | 276 | data: list, |
fd9aa8df DC |
277 | }, |
278 | columns: [ | |
279 | { | |
e1beaae4 DM |
280 | text: gettext('Property'), |
281 | dataIndex: 'key', | |
282 | flex: 1, | |
fd9aa8df DC |
283 | }, |
284 | { | |
285 | text: gettext('Value'), | |
286 | dataIndex: 'value', | |
287 | flex: 1, | |
288 | }, | |
289 | ], | |
290 | }, | |
291 | ], | |
292 | }).show(); | |
293 | }); | |
294 | }, | |
295 | ||
296 | readLabel: function(view, rI, cI, button, el, record) { | |
297 | let me = this; | |
298 | let drive = record.data.name; | |
299 | me.driveCommand(drive, 'read-label', function(response) { | |
965bd586 DM |
300 | let list = []; |
301 | for (let [key, val] of Object.entries(response.result.data)) { | |
302 | if (key === 'ctime' || key === 'media-set-ctime') { | |
303 | val = Proxmox.Utils.render_timestamp(val); | |
304 | } | |
305 | list.push({ key: key, value: val }); | |
fd9aa8df DC |
306 | } |
307 | ||
965bd586 | 308 | Ext.create('Ext.window.Window', { |
fd9aa8df | 309 | title: gettext('Label Information'), |
965bd586 DM |
310 | modal: true, |
311 | width: 600, | |
312 | height: 450, | |
313 | layout: 'fit', | |
314 | scrollable: true, | |
315 | items: [ | |
316 | { | |
317 | xtype: 'grid', | |
318 | store: { | |
319 | data: list, | |
320 | }, | |
321 | columns: [ | |
322 | { | |
323 | text: gettext('Property'), | |
324 | dataIndex: 'key', | |
325 | width: 120, | |
326 | }, | |
327 | { | |
328 | text: gettext('Value'), | |
329 | dataIndex: 'value', | |
330 | flex: 1, | |
331 | }, | |
332 | ], | |
333 | }, | |
334 | ], | |
335 | }).show(); | |
fd9aa8df DC |
336 | }); |
337 | }, | |
338 | ||
339 | status: function(view, rI, cI, button, el, record) { | |
340 | let me = this; | |
341 | let drive = record.data.name; | |
342 | me.driveCommand(drive, 'status', function(response) { | |
35a7ab57 DM |
343 | let list = []; |
344 | for (let [key, val] of Object.entries(response.result.data)) { | |
345 | if (key === 'manufactured') { | |
346 | val = Proxmox.Utils.render_timestamp(val); | |
347 | } | |
348 | if (key === 'bytes-read' || key === 'bytes-written') { | |
349 | val = Proxmox.Utils.format_size(val); | |
350 | } | |
351 | list.push({ key: key, value: val }); | |
fd9aa8df DC |
352 | } |
353 | ||
35a7ab57 | 354 | Ext.create('Ext.window.Window', { |
4d651378 | 355 | title: gettext('Status'), |
35a7ab57 DM |
356 | modal: true, |
357 | width: 600, | |
358 | height: 450, | |
359 | layout: 'fit', | |
360 | scrollable: true, | |
361 | items: [ | |
362 | { | |
363 | xtype: 'grid', | |
364 | store: { | |
365 | data: list, | |
366 | }, | |
367 | columns: [ | |
368 | { | |
369 | text: gettext('Property'), | |
370 | dataIndex: 'key', | |
371 | width: 120, | |
372 | }, | |
373 | { | |
374 | text: gettext('Value'), | |
375 | dataIndex: 'value', | |
376 | flex: 1, | |
377 | }, | |
378 | ], | |
379 | }, | |
380 | ], | |
381 | }).show(); | |
fd9aa8df DC |
382 | }); |
383 | }, | |
384 | ||
385 | reloadList: function() { | |
386 | let me = this; | |
387 | me.lookup('changerselector').getStore().load(); | |
388 | }, | |
389 | ||
390 | barcodeLabel: function() { | |
391 | let me = this; | |
392 | let vm = me.getViewModel(); | |
393 | let changer = vm.get('changer'); | |
394 | if (changer === '') { | |
395 | return; | |
396 | } | |
397 | ||
398 | Ext.create('Proxmox.window.Edit', { | |
399 | title: gettext('Barcode Label'), | |
400 | showTaskViewer: true, | |
b8d526f1 | 401 | method: 'POST', |
fd9aa8df DC |
402 | url: '/api2/extjs/tape/drive', |
403 | submitUrl: function(url, values) { | |
404 | let drive = values.drive; | |
405 | delete values.drive; | |
406 | return `${url}/${encodeURIComponent(drive)}/barcode-label-media`; | |
407 | }, | |
408 | ||
409 | items: [ | |
410 | { | |
411 | xtype: 'pbsDriveSelector', | |
412 | fieldLabel: gettext('Drive'), | |
413 | name: 'drive', | |
414 | changer: changer, | |
415 | }, | |
416 | { | |
417 | xtype: 'pbsMediaPoolSelector', | |
418 | fieldLabel: gettext('Pool'), | |
419 | name: 'pool', | |
420 | skipEmptyText: true, | |
421 | allowBlank: true, | |
422 | }, | |
423 | ], | |
424 | }).show(); | |
425 | }, | |
426 | ||
e2225aa8 DC |
427 | inventory: function() { |
428 | let me = this; | |
429 | let vm = me.getViewModel(); | |
430 | let changer = vm.get('changer'); | |
431 | if (changer === '') { | |
432 | return; | |
433 | } | |
434 | ||
435 | Ext.create('Proxmox.window.Edit', { | |
436 | title: gettext('Inventory'), | |
437 | showTaskViewer: true, | |
438 | method: 'PUT', | |
439 | url: '/api2/extjs/tape/drive', | |
440 | submitUrl: function(url, values) { | |
441 | let drive = values.drive; | |
442 | delete values.drive; | |
443 | return `${url}/${encodeURIComponent(drive)}/inventory`; | |
444 | }, | |
445 | ||
446 | items: [ | |
447 | { | |
448 | xtype: 'pbsDriveSelector', | |
449 | fieldLabel: gettext('Drive'), | |
450 | name: 'drive', | |
451 | changer: changer, | |
452 | }, | |
453 | ], | |
454 | }).show(); | |
455 | }, | |
456 | ||
076afa61 DC |
457 | scheduleReload: function(time) { |
458 | let me = this; | |
459 | if (me.reloadTimeout === undefined) { | |
460 | me.reloadTimeout = setTimeout(function() { | |
461 | me.reload(); | |
462 | }, time); | |
463 | } | |
464 | }, | |
465 | ||
cbd98993 | 466 | reload: function() { |
076afa61 DC |
467 | let me = this; |
468 | if (me.reloadTimeout !== undefined) { | |
469 | clearTimeout(me.reloadTimeout); | |
470 | me.reloadTimeout = undefined; | |
471 | } | |
472 | me.reload_full(true); | |
cbd98993 DM |
473 | }, |
474 | ||
475 | reload_no_cache: function() { | |
076afa61 DC |
476 | let me = this; |
477 | if (me.reloadTimeout !== undefined) { | |
478 | clearTimeout(me.reloadTimeout); | |
479 | me.reloadTimeout = undefined; | |
480 | } | |
481 | me.reload_full(false); | |
cbd98993 DM |
482 | }, |
483 | ||
484 | reload_full: async function(use_cache) { | |
fd9aa8df DC |
485 | let me = this; |
486 | let view = me.getView(); | |
487 | let vm = me.getViewModel(); | |
488 | let changer = vm.get('changer'); | |
489 | if (changer === '') { | |
490 | return; | |
491 | } | |
492 | ||
493 | try { | |
423e3cbd DC |
494 | if (!use_cache) { |
495 | Proxmox.Utils.setErrorMask(view, true); | |
496 | Proxmox.Utils.setErrorMask(me.lookup('content')); | |
497 | } | |
42eef145 | 498 | let status_fut = PBS.Async.api2({ |
b0338178 | 499 | timeout: 5*60*1000, |
cbd98993 | 500 | method: 'GET', |
fd9aa8df | 501 | url: `/api2/extjs/tape/changer/${encodeURIComponent(changer)}/status`, |
cbd98993 DM |
502 | params: { |
503 | cache: use_cache, | |
504 | }, | |
fd9aa8df | 505 | }); |
42eef145 | 506 | let drives_fut = PBS.Async.api2({ |
b0338178 | 507 | timeout: 5*60*1000, |
fd9aa8df DC |
508 | url: `/api2/extjs/tape/drive?changer=${encodeURIComponent(changer)}`, |
509 | }); | |
510 | ||
42eef145 | 511 | let tapes_fut = PBS.Async.api2({ |
b0338178 | 512 | timeout: 5*60*1000, |
42eef145 | 513 | url: '/api2/extjs/tape/media/list', |
aa16b7b2 DM |
514 | method: 'GET', |
515 | params: { | |
6ef8e290 | 516 | "update-status": false, |
6d62e69f | 517 | }, |
42eef145 DC |
518 | }); |
519 | ||
520 | let [status, drives, tapes_list] = await Promise.all([status_fut, drives_fut, tapes_fut]); | |
521 | ||
fd9aa8df DC |
522 | let data = { |
523 | slot: [], | |
524 | 'import-export': [], | |
525 | drive: [], | |
526 | }; | |
527 | ||
42eef145 DC |
528 | let tapes = {}; |
529 | ||
530 | for (const tape of tapes_list.result.data) { | |
bc02c278 DC |
531 | tapes[tape['label-text']] = { |
532 | labeled: true, | |
533 | pool: tape.pool, | |
534 | status: tape.expired ? 'expired' : tape.status, | |
535 | }; | |
42eef145 DC |
536 | } |
537 | ||
fd9aa8df DC |
538 | let drive_entries = {}; |
539 | ||
540 | for (const entry of drives.result.data) { | |
541 | drive_entries[entry['changer-drivenum'] || 0] = entry; | |
542 | } | |
543 | ||
544 | for (let entry of status.result.data) { | |
545 | let type = entry['entry-kind']; | |
546 | ||
547 | if (type === 'drive' && drive_entries[entry['entry-id']] !== undefined) { | |
548 | entry = Ext.applyIf(entry, drive_entries[entry['entry-id']]); | |
549 | } | |
550 | ||
bc02c278 DC |
551 | if (tapes[entry['label-text']] !== undefined) { |
552 | entry['is-labeled'] = true; | |
553 | entry.pool = tapes[entry['label-text']].pool; | |
554 | entry.status = tapes[entry['label-text']].status; | |
555 | } else { | |
556 | entry['is-labeled'] = false; | |
557 | } | |
42eef145 | 558 | |
fd9aa8df DC |
559 | data[type].push(entry); |
560 | } | |
561 | ||
a83cedc2 DC |
562 | // the stores are diffstores and are only refreshed |
563 | // on a 'load' event, which does not trigger on 'setData' | |
564 | // so we have to fire them ourselves | |
fd9aa8df | 565 | |
a83cedc2 DC |
566 | me.lookup('slots').getStore().rstore.setData(data.slot); |
567 | me.lookup('slots').getStore().rstore.fireEvent('load', me, [], true); | |
568 | ||
569 | me.lookup('import_export').getStore().rstore.setData(data['import-export']); | |
570 | me.lookup('import_export').getStore().rstore.fireEvent('load', me, [], true); | |
571 | ||
572 | me.lookup('drives').getStore().rstore.setData(data.drive); | |
573 | me.lookup('drives').getStore().rstore.fireEvent('load', me, [], true); | |
fd9aa8df | 574 | |
423e3cbd DC |
575 | if (!use_cache) { |
576 | Proxmox.Utils.setErrorMask(view); | |
577 | } | |
578 | Proxmox.Utils.setErrorMask(me.lookup('content')); | |
fd9aa8df | 579 | } catch (err) { |
423e3cbd DC |
580 | if (!use_cache) { |
581 | Proxmox.Utils.setErrorMask(view); | |
582 | } | |
583 | Proxmox.Utils.setErrorMask(me.lookup('content'), err.toString()); | |
fd9aa8df | 584 | } |
076afa61 DC |
585 | |
586 | me.scheduleReload(5000); | |
fd9aa8df | 587 | }, |
4094fe5a DC |
588 | |
589 | renderIsLabeled: function(value, mD, record) { | |
590 | if (!record.data['label-text']) { | |
591 | return ""; | |
592 | } | |
593 | ||
594 | if (record.data['label-text'].startsWith("CLN")) { | |
595 | return ""; | |
596 | } | |
597 | ||
598 | if (!value) { | |
599 | return gettext('Not Labeled'); | |
600 | } | |
601 | ||
602 | let status = record.data.status; | |
603 | if (record.data.pool) { | |
604 | return `${status} (${record.data.pool})`; | |
605 | } | |
606 | return status; | |
607 | }, | |
0d890ec4 DC |
608 | |
609 | renderState: function(value, md, record) { | |
610 | if (!value) { | |
611 | return gettext('Idle'); | |
612 | } | |
613 | ||
614 | let icon = '<i class="fa fa-spinner fa-pulse fa-fw"></i>'; | |
615 | ||
616 | if (value.startsWith("UPID")) { | |
617 | let upid = Proxmox.Utils.parse_task_upid(value); | |
618 | md.tdCls = "pointer"; | |
619 | return `${icon} ${upid.desc}`; | |
620 | } | |
621 | ||
622 | return `${icon} ${value}`; | |
623 | }, | |
624 | ||
625 | control: { | |
626 | 'grid[reference=drives]': { | |
627 | cellclick: function(table, td, ci, rec, tr, ri, e) { | |
f26276bc | 628 | if (e.position.column.dataIndex !== 'state') { |
0d890ec4 DC |
629 | return; |
630 | } | |
631 | ||
632 | let upid = rec.data.state; | |
633 | if (!upid || !upid.startsWith("UPID")) { | |
634 | return; | |
635 | } | |
636 | ||
637 | Ext.create('Proxmox.window.TaskViewer', { | |
638 | autoShow: true, | |
639 | upid, | |
640 | }); | |
641 | }, | |
642 | }, | |
643 | }, | |
fd9aa8df DC |
644 | }, |
645 | ||
646 | listeners: { | |
647 | activate: 'reload', | |
648 | }, | |
649 | ||
650 | tbar: [ | |
651 | { | |
652 | fieldLabel: gettext('Changer'), | |
653 | xtype: 'pbsChangerSelector', | |
654 | reference: 'changerselector', | |
655 | autoSelect: true, | |
656 | listeners: { | |
657 | change: 'changerChange', | |
658 | }, | |
659 | }, | |
660 | '-', | |
661 | { | |
662 | text: gettext('Reload'), | |
663 | xtype: 'proxmoxButton', | |
cbd98993 | 664 | handler: 'reload_no_cache', |
fd9aa8df DC |
665 | selModel: false, |
666 | }, | |
667 | '-', | |
fd9aa8df DC |
668 | { |
669 | text: gettext('Barcode Label'), | |
670 | xtype: 'proxmoxButton', | |
671 | handler: 'barcodeLabel', | |
672 | iconCls: 'fa fa-barcode', | |
673 | bind: { | |
674 | disabled: '{!changerSelected}', | |
675 | }, | |
676 | }, | |
e2225aa8 DC |
677 | { |
678 | text: gettext('Inventory'), | |
679 | xtype: 'proxmoxButton', | |
680 | handler: 'inventory', | |
681 | iconCls: 'fa fa-book', | |
682 | bind: { | |
683 | disabled: '{!changerSelected}', | |
684 | }, | |
685 | }, | |
fd9aa8df DC |
686 | ], |
687 | ||
688 | layout: 'auto', | |
689 | bodyPadding: 5, | |
690 | scrollable: true, | |
691 | ||
692 | items: [ | |
693 | { | |
694 | xtype: 'container', | |
695 | reference: 'content', | |
696 | layout: { | |
697 | type: 'hbox', | |
698 | aling: 'stretch', | |
699 | }, | |
700 | items: [ | |
701 | { | |
702 | xtype: 'grid', | |
703 | reference: 'slots', | |
704 | title: gettext('Slots'), | |
705 | padding: 5, | |
706 | flex: 1, | |
707 | store: { | |
a83cedc2 DC |
708 | type: 'diff', |
709 | rstore: { | |
710 | type: 'store', | |
711 | model: 'pbs-slot-model', | |
712 | }, | |
fd9aa8df DC |
713 | data: [], |
714 | }, | |
715 | columns: [ | |
716 | { | |
970a70b4 | 717 | text: gettext('ID'), |
fd9aa8df DC |
718 | dataIndex: 'entry-id', |
719 | width: 50, | |
720 | }, | |
721 | { | |
722 | text: gettext("Content"), | |
723 | dataIndex: 'label-text', | |
724 | flex: 1, | |
725 | renderer: (value) => value || '', | |
726 | }, | |
42eef145 | 727 | { |
bc02c278 | 728 | text: gettext('Inventory'), |
42eef145 | 729 | dataIndex: 'is-labeled', |
4094fe5a | 730 | renderer: 'renderIsLabeled', |
bc02c278 | 731 | flex: 1, |
42eef145 | 732 | }, |
fd9aa8df DC |
733 | { |
734 | text: gettext('Actions'), | |
735 | xtype: 'actioncolumn', | |
736 | width: 100, | |
737 | items: [ | |
738 | { | |
739 | iconCls: 'fa fa-rotate-90 fa-exchange', | |
740 | handler: 'slotTransfer', | |
d2edc68e | 741 | tooltip: gettext('Transfer'), |
fd9aa8df DC |
742 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'], |
743 | }, | |
85205bc2 DC |
744 | { |
745 | iconCls: 'fa fa-trash-o', | |
746 | handler: 'erase', | |
747 | tooltip: gettext('Erase'), | |
748 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'], | |
749 | }, | |
fd9aa8df DC |
750 | { |
751 | iconCls: 'fa fa-rotate-90 fa-upload', | |
752 | handler: 'load', | |
d2edc68e | 753 | tooltip: gettext('Load'), |
fd9aa8df DC |
754 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'], |
755 | }, | |
756 | ], | |
757 | }, | |
758 | ], | |
759 | }, | |
760 | { | |
761 | xtype: 'container', | |
762 | flex: 2, | |
763 | defaults: { | |
764 | padding: 5, | |
765 | }, | |
766 | items: [ | |
767 | { | |
768 | xtype: 'grid', | |
769 | reference: 'drives', | |
770 | title: gettext('Drives'), | |
771 | store: { | |
a83cedc2 DC |
772 | type: 'diff', |
773 | rstore: { | |
774 | type: 'store', | |
775 | model: 'pbs-slot-model', | |
776 | }, | |
fd9aa8df DC |
777 | data: [], |
778 | }, | |
779 | columns: [ | |
780 | { | |
970a70b4 | 781 | text: gettext('ID'), |
fd9aa8df | 782 | dataIndex: 'entry-id', |
970a70b4 | 783 | hidden: true, |
fd9aa8df DC |
784 | width: 50, |
785 | }, | |
786 | { | |
787 | text: gettext("Content"), | |
788 | dataIndex: 'label-text', | |
789 | flex: 1, | |
790 | renderer: (value) => value || '', | |
791 | }, | |
4094fe5a DC |
792 | { |
793 | text: gettext('Inventory'), | |
794 | dataIndex: 'is-labeled', | |
795 | renderer: 'renderIsLabeled', | |
0d890ec4 | 796 | flex: 1.5, |
4094fe5a | 797 | }, |
fd9aa8df DC |
798 | { |
799 | text: gettext("Name"), | |
800 | sortable: true, | |
801 | dataIndex: 'name', | |
802 | flex: 1, | |
803 | renderer: Ext.htmlEncode, | |
804 | }, | |
0d890ec4 DC |
805 | { |
806 | text: gettext('State'), | |
807 | dataIndex: 'state', | |
808 | flex: 3, | |
809 | renderer: 'renderState', | |
810 | }, | |
fd9aa8df DC |
811 | { |
812 | text: gettext("Vendor"), | |
813 | sortable: true, | |
814 | dataIndex: 'vendor', | |
0d890ec4 | 815 | hidden: true, |
fd9aa8df DC |
816 | flex: 1, |
817 | renderer: Ext.htmlEncode, | |
818 | }, | |
819 | { | |
820 | text: gettext("Model"), | |
821 | sortable: true, | |
822 | dataIndex: 'model', | |
0d890ec4 | 823 | hidden: true, |
fd9aa8df DC |
824 | flex: 1, |
825 | renderer: Ext.htmlEncode, | |
826 | }, | |
827 | { | |
828 | text: gettext("Serial"), | |
829 | sortable: true, | |
830 | dataIndex: 'serial', | |
0d890ec4 | 831 | hidden: true, |
fd9aa8df DC |
832 | flex: 1, |
833 | renderer: Ext.htmlEncode, | |
834 | }, | |
835 | { | |
836 | xtype: 'actioncolumn', | |
837 | text: gettext('Actions'), | |
838 | width: 140, | |
839 | items: [ | |
840 | { | |
841 | iconCls: 'fa fa-rotate-270 fa-upload', | |
842 | handler: 'unload', | |
d2edc68e | 843 | tooltip: gettext('Unload'), |
e63457b6 | 844 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'] || rec.data['is-blocked'], |
fd9aa8df DC |
845 | }, |
846 | { | |
847 | iconCls: 'fa fa-hdd-o', | |
848 | handler: 'cartridgeMemory', | |
d2edc68e | 849 | tooltip: gettext('Cartridge Memory'), |
e63457b6 | 850 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'] || rec.data['is-blocked'], |
fd9aa8df DC |
851 | }, |
852 | { | |
853 | iconCls: 'fa fa-line-chart', | |
854 | handler: 'volumeStatistics', | |
d2edc68e | 855 | tooltip: gettext('Volume Statistics'), |
e63457b6 | 856 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'] || rec.data['is-blocked'], |
fd9aa8df DC |
857 | }, |
858 | { | |
859 | iconCls: 'fa fa-tag', | |
860 | handler: 'readLabel', | |
d2edc68e | 861 | tooltip: gettext('Read Label'), |
e63457b6 | 862 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'] || rec.data['is-blocked'], |
fd9aa8df DC |
863 | }, |
864 | { | |
865 | iconCls: 'fa fa-info-circle', | |
d2edc68e | 866 | tooltip: gettext('Status'), |
fd9aa8df | 867 | handler: 'status', |
e63457b6 | 868 | isDisabled: (v, r, c, i, rec) => rec.data['is-blocked'], |
fd9aa8df DC |
869 | }, |
870 | { | |
871 | iconCls: 'fa fa-shower', | |
d2edc68e | 872 | tooltip: gettext('Clean Drive'), |
fd9aa8df | 873 | handler: 'cleanDrive', |
e63457b6 | 874 | isDisabled: (v, r, c, i, rec) => rec.data['is-blocked'], |
fd9aa8df DC |
875 | }, |
876 | ], | |
877 | }, | |
878 | ], | |
879 | }, | |
880 | { | |
881 | xtype: 'grid', | |
882 | reference: 'import_export', | |
883 | store: { | |
a83cedc2 DC |
884 | type: 'diff', |
885 | rstore: { | |
886 | type: 'store', | |
887 | model: 'pbs-slot-model', | |
888 | }, | |
fd9aa8df DC |
889 | data: [], |
890 | }, | |
970a70b4 | 891 | title: gettext('Import-Export Slots'), |
fd9aa8df DC |
892 | columns: [ |
893 | { | |
970a70b4 | 894 | text: gettext('ID'), |
fd9aa8df DC |
895 | dataIndex: 'entry-id', |
896 | width: 50, | |
897 | }, | |
898 | { | |
899 | text: gettext("Content"), | |
900 | dataIndex: 'label-text', | |
901 | renderer: (value) => value || '', | |
902 | flex: 1, | |
903 | }, | |
4094fe5a DC |
904 | { |
905 | text: gettext('Inventory'), | |
906 | dataIndex: 'is-labeled', | |
907 | renderer: 'renderIsLabeled', | |
908 | flex: 1, | |
909 | }, | |
fd9aa8df DC |
910 | { |
911 | text: gettext('Actions'), | |
58791864 DC |
912 | xtype: 'actioncolumn', |
913 | items: [ | |
914 | { | |
915 | iconCls: 'fa fa-rotate-270 fa-upload', | |
916 | handler: 'importTape', | |
917 | tooltip: gettext('Import'), | |
918 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'], | |
919 | }, | |
920 | ], | |
fd9aa8df DC |
921 | width: 80, |
922 | }, | |
923 | ], | |
924 | }, | |
925 | ], | |
926 | }, | |
927 | ], | |
928 | }, | |
929 | ], | |
930 | }); |