]>
Commit | Line | Data |
---|---|---|
fd9aa8df DC |
1 | Ext.define('PBS.TapeManagement.ChangerStatus', { |
2 | extend: 'Ext.panel.Panel', | |
3 | alias: 'widget.pbsChangerStatus', | |
4 | ||
5 | viewModel: { | |
6 | data: { | |
7 | changer: '', | |
8 | }, | |
9 | ||
10 | formulas: { | |
11 | changerSelected: (get) => get('changer') !== '', | |
12 | }, | |
13 | }, | |
14 | ||
15 | controller: { | |
16 | xclass: 'Ext.app.ViewController', | |
17 | ||
18 | changerChange: function(field, value) { | |
19 | let me = this; | |
20 | let view = me.getView(); | |
21 | let vm = me.getViewModel(); | |
22 | vm.set('changer', value); | |
23 | if (view.rendered) { | |
24 | me.reload(); | |
25 | } | |
26 | }, | |
27 | ||
28 | onAdd: function() { | |
29 | let me = this; | |
30 | Ext.create('PBS.TapeManagement.ChangerEditWindow', { | |
31 | listeners: { | |
32 | destroy: function() { | |
33 | me.reloadList(); | |
34 | }, | |
35 | }, | |
36 | }).show(); | |
37 | }, | |
38 | ||
39 | onEdit: function() { | |
40 | let me = this; | |
41 | let vm = me.getViewModel(); | |
42 | let changerid = vm.get('changer'); | |
43 | Ext.create('PBS.TapeManagement.ChangerEditWindow', { | |
44 | changerid, | |
45 | autoLoad: true, | |
46 | listeners: { | |
47 | destroy: () => me.reload(), | |
48 | }, | |
49 | }).show(); | |
50 | }, | |
51 | ||
52 | slotTransfer: function(view, rI, cI, button, el, record) { | |
53 | let me = this; | |
54 | let vm = me.getViewModel(); | |
55 | let from = record.data['entry-id']; | |
56 | let changer = encodeURIComponent(vm.get('changer')); | |
57 | Ext.create('Proxmox.window.Edit', { | |
58 | title: gettext('Transfer'), | |
59 | isCreate: true, | |
60 | submitText: gettext('OK'), | |
61 | method: 'POST', | |
62 | url: `/api2/extjs/tape/changer/${changer}/transfer`, | |
63 | items: [ | |
64 | { | |
65 | xtype: 'displayfield', | |
66 | name: 'from', | |
67 | value: from, | |
68 | submitValue: true, | |
69 | fieldLabel: gettext('From Slot'), | |
70 | }, | |
71 | { | |
72 | xtype: 'proxmoxintegerfield', | |
73 | name: 'to', | |
74 | fieldLabel: gettext('To Slot'), | |
75 | }, | |
76 | ], | |
77 | listeners: { | |
78 | destroy: function() { | |
79 | me.reload(); | |
80 | }, | |
81 | }, | |
82 | }).show(); | |
83 | }, | |
84 | ||
85 | load: function(view, rI, cI, button, el, record) { | |
86 | let me = this; | |
87 | let vm = me.getViewModel(); | |
88 | let label = record.data['label-text']; | |
89 | ||
90 | let changer = vm.get('changer'); | |
91 | ||
92 | Ext.create('Proxmox.window.Edit', { | |
93 | isCreate: true, | |
94 | submitText: gettext('OK'), | |
95 | title: gettext('Load Media into Drive'), | |
96 | url: `/api2/extjs/tape/drive`, | |
97 | submitUrl: function(url, values) { | |
98 | let drive = values.drive; | |
99 | delete values.drive; | |
100 | return `${url}/${encodeURIComponent(drive)}/load-media`; | |
101 | }, | |
102 | items: [ | |
103 | { | |
104 | xtype: 'displayfield', | |
105 | name: 'label-text', | |
106 | value: label, | |
107 | submitValue: true, | |
108 | fieldLabel: gettext('Media'), | |
109 | }, | |
110 | { | |
111 | xtype: 'pbsDriveSelector', | |
112 | fieldLabel: gettext('Drive'), | |
113 | changer: changer, | |
114 | name: 'drive', | |
115 | }, | |
116 | ], | |
117 | listeners: { | |
118 | destroy: function() { | |
119 | me.reload(); | |
120 | }, | |
121 | }, | |
122 | }).show(); | |
123 | }, | |
124 | ||
125 | unload: async function(view, rI, cI, button, el, record) { | |
126 | let me = this; | |
127 | let drive = record.data.name; | |
128 | Proxmox.Utils.setErrorMask(view, true); | |
129 | try { | |
130 | await PBS.Async.api2({ | |
131 | method: 'PUT', | |
132 | url: `/api2/extjs/tape/drive/${encodeURIComponent(drive)}/unload`, | |
133 | }); | |
134 | Proxmox.Utils.setErrorMask(view); | |
135 | me.reload(); | |
136 | } catch (error) { | |
137 | Ext.Msg.alert(gettext('Error'), error); | |
138 | Proxmox.Utils.setErrorMask(view); | |
139 | me.reload(); | |
140 | } | |
141 | }, | |
142 | ||
143 | driveCommand: function(driveid, command, callback, params, method) { | |
144 | let me = this; | |
145 | let view = me.getView(); | |
146 | params = params || {}; | |
147 | method = method || 'GET'; | |
148 | Proxmox.Utils.API2Request({ | |
149 | url: `/api2/extjs/tape/drive/${driveid}/${command}`, | |
150 | method, | |
151 | waitMsgTarget: view, | |
152 | params, | |
153 | success: function(response) { | |
154 | callback(response); | |
155 | }, | |
156 | failure: function(response) { | |
157 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
158 | }, | |
159 | }); | |
160 | }, | |
161 | ||
162 | cartridgeMemory: function(view, rI, cI, button, el, record) { | |
163 | let me = this; | |
164 | let drive = record.data.name; | |
165 | me.driveCommand(drive, 'cartridge-memory', function(response) { | |
166 | Ext.create('Ext.window.Window', { | |
167 | title: gettext('Cartridge Memory'), | |
168 | modal: true, | |
169 | width: 600, | |
170 | height: 450, | |
171 | layout: 'fit', | |
172 | scrollable: true, | |
173 | items: [ | |
174 | { | |
175 | xtype: 'grid', | |
176 | store: { | |
177 | data: response.result.data, | |
178 | }, | |
179 | columns: [ | |
180 | { | |
181 | text: gettext('ID'), | |
182 | dataIndex: 'id', | |
183 | width: 60, | |
184 | }, | |
185 | { | |
186 | text: gettext('Name'), | |
187 | dataIndex: 'name', | |
188 | flex: 2, | |
189 | }, | |
190 | { | |
191 | text: gettext('Value'), | |
192 | dataIndex: 'value', | |
193 | flex: 1, | |
194 | }, | |
195 | ], | |
196 | }, | |
197 | ], | |
198 | }).show(); | |
199 | }); | |
200 | }, | |
201 | ||
202 | cleanDrive: function(view, rI, cI, button, el, record) { | |
203 | let me = this; | |
204 | let drive = record.data.name; | |
205 | me.driveCommand(drive, 'clean', function(response) { | |
206 | Ext.create('Proxmox.window.TaskProgress', { | |
207 | upid: response.result.data, | |
208 | taskDone: function() { | |
209 | me.reload(); | |
210 | }, | |
211 | }).show(); | |
212 | }, {}, 'PUT'); | |
213 | }, | |
214 | ||
215 | volumeStatistics: function(view, rI, cI, button, el, record) { | |
216 | let me = this; | |
217 | let drive = record.data.name; | |
218 | me.driveCommand(drive, 'volume-statistics', function(response) { | |
219 | Ext.create('Ext.window.Window', { | |
220 | title: gettext('Volume Statistics'), | |
221 | modal: true, | |
222 | width: 600, | |
223 | height: 450, | |
224 | layout: 'fit', | |
225 | scrollable: true, | |
226 | items: [ | |
227 | { | |
228 | xtype: 'grid', | |
229 | store: { | |
230 | data: response.result.data, | |
231 | }, | |
232 | columns: [ | |
233 | { | |
234 | text: gettext('ID'), | |
235 | dataIndex: 'id', | |
236 | width: 60, | |
237 | }, | |
238 | { | |
239 | text: gettext('Name'), | |
240 | dataIndex: 'name', | |
241 | flex: 2, | |
242 | }, | |
243 | { | |
244 | text: gettext('Value'), | |
245 | dataIndex: 'value', | |
246 | flex: 1, | |
247 | }, | |
248 | ], | |
249 | }, | |
250 | ], | |
251 | }).show(); | |
252 | }); | |
253 | }, | |
254 | ||
255 | readLabel: function(view, rI, cI, button, el, record) { | |
256 | let me = this; | |
257 | let drive = record.data.name; | |
258 | me.driveCommand(drive, 'read-label', function(response) { | |
259 | let lines = []; | |
260 | for (const [key, val] of Object.entries(response.result.data)) { | |
261 | lines.push(`${key}: ${val}`); | |
262 | } | |
263 | ||
264 | let txt = lines.join('<br>'); | |
265 | ||
266 | Ext.Msg.show({ | |
267 | title: gettext('Label Information'), | |
268 | message: txt, | |
269 | icon: undefined, | |
270 | }); | |
271 | }); | |
272 | }, | |
273 | ||
274 | status: function(view, rI, cI, button, el, record) { | |
275 | let me = this; | |
276 | let drive = record.data.name; | |
277 | me.driveCommand(drive, 'status', function(response) { | |
278 | let lines = []; | |
279 | for (const [key, val] of Object.entries(response.result.data)) { | |
280 | lines.push(`${key}: ${val}`); | |
281 | } | |
282 | ||
283 | let txt = lines.join('<br>'); | |
284 | ||
285 | Ext.Msg.show({ | |
286 | title: gettext('Label Information'), | |
287 | message: txt, | |
288 | icon: undefined, | |
289 | }); | |
290 | }); | |
291 | }, | |
292 | ||
293 | reloadList: function() { | |
294 | let me = this; | |
295 | me.lookup('changerselector').getStore().load(); | |
296 | }, | |
297 | ||
298 | barcodeLabel: function() { | |
299 | let me = this; | |
300 | let vm = me.getViewModel(); | |
301 | let changer = vm.get('changer'); | |
302 | if (changer === '') { | |
303 | return; | |
304 | } | |
305 | ||
306 | Ext.create('Proxmox.window.Edit', { | |
307 | title: gettext('Barcode Label'), | |
308 | showTaskViewer: true, | |
309 | url: '/api2/extjs/tape/drive', | |
310 | submitUrl: function(url, values) { | |
311 | let drive = values.drive; | |
312 | delete values.drive; | |
313 | return `${url}/${encodeURIComponent(drive)}/barcode-label-media`; | |
314 | }, | |
315 | ||
316 | items: [ | |
317 | { | |
318 | xtype: 'pbsDriveSelector', | |
319 | fieldLabel: gettext('Drive'), | |
320 | name: 'drive', | |
321 | changer: changer, | |
322 | }, | |
323 | { | |
324 | xtype: 'pbsMediaPoolSelector', | |
325 | fieldLabel: gettext('Pool'), | |
326 | name: 'pool', | |
327 | skipEmptyText: true, | |
328 | allowBlank: true, | |
329 | }, | |
330 | ], | |
331 | }).show(); | |
332 | }, | |
333 | ||
334 | reload: async function() { | |
335 | let me = this; | |
336 | let view = me.getView(); | |
337 | let vm = me.getViewModel(); | |
338 | let changer = vm.get('changer'); | |
339 | if (changer === '') { | |
340 | return; | |
341 | } | |
342 | ||
343 | try { | |
344 | Proxmox.Utils.setErrorMask(view, true); | |
345 | Proxmox.Utils.setErrorMask(me.lookup('content')); | |
346 | let status = await PBS.Async.api2({ | |
347 | url: `/api2/extjs/tape/changer/${encodeURIComponent(changer)}/status`, | |
348 | }); | |
349 | let drives = await PBS.Async.api2({ | |
350 | url: `/api2/extjs/tape/drive?changer=${encodeURIComponent(changer)}`, | |
351 | }); | |
352 | ||
353 | let data = { | |
354 | slot: [], | |
355 | 'import-export': [], | |
356 | drive: [], | |
357 | }; | |
358 | ||
359 | let drive_entries = {}; | |
360 | ||
361 | for (const entry of drives.result.data) { | |
362 | drive_entries[entry['changer-drivenum'] || 0] = entry; | |
363 | } | |
364 | ||
365 | for (let entry of status.result.data) { | |
366 | let type = entry['entry-kind']; | |
367 | ||
368 | if (type === 'drive' && drive_entries[entry['entry-id']] !== undefined) { | |
369 | entry = Ext.applyIf(entry, drive_entries[entry['entry-id']]); | |
370 | } | |
371 | ||
372 | data[type].push(entry); | |
373 | } | |
374 | ||
375 | ||
376 | me.lookup('slots').getStore().setData(data.slot); | |
377 | me.lookup('import_export').getStore().setData(data['import-export']); | |
378 | me.lookup('drives').getStore().setData(data.drive); | |
379 | ||
380 | Proxmox.Utils.setErrorMask(view); | |
381 | } catch (err) { | |
382 | Proxmox.Utils.setErrorMask(view); | |
383 | Proxmox.Utils.setErrorMask(me.lookup('content'), err); | |
384 | } | |
385 | }, | |
386 | }, | |
387 | ||
388 | listeners: { | |
389 | activate: 'reload', | |
390 | }, | |
391 | ||
392 | tbar: [ | |
393 | { | |
394 | fieldLabel: gettext('Changer'), | |
395 | xtype: 'pbsChangerSelector', | |
396 | reference: 'changerselector', | |
397 | autoSelect: true, | |
398 | listeners: { | |
399 | change: 'changerChange', | |
400 | }, | |
401 | }, | |
402 | '-', | |
403 | { | |
404 | text: gettext('Reload'), | |
405 | xtype: 'proxmoxButton', | |
406 | handler: 'reload', | |
407 | selModel: false, | |
408 | }, | |
409 | '-', | |
410 | { | |
411 | text: gettext('Add'), | |
412 | xtype: 'proxmoxButton', | |
413 | handler: 'onAdd', | |
414 | selModel: false, | |
415 | }, | |
416 | { | |
417 | text: gettext('Edit'), | |
418 | xtype: 'proxmoxButton', | |
419 | handler: 'onEdit', | |
420 | bind: { | |
421 | disabled: '{!changerSelected}', | |
422 | }, | |
423 | }, | |
424 | { | |
425 | xtype: 'proxmoxStdRemoveButton', | |
426 | baseurl: '/api2/extjs/config/changer', | |
427 | callback: 'reloadList', | |
428 | selModel: false, | |
429 | getRecordName: function() { | |
430 | let me = this; | |
431 | let vm = me.up('panel').getViewModel(); | |
432 | return vm.get('changer'); | |
433 | }, | |
434 | getUrl: function() { | |
435 | let me = this; | |
436 | let vm = me.up('panel').getViewModel(); | |
437 | return `/api2/extjs/config/changer/${vm.get('changer')}`; | |
438 | }, | |
439 | bind: { | |
440 | disabled: '{!changerSelected}', | |
441 | }, | |
442 | }, | |
443 | '-', | |
444 | { | |
445 | text: gettext('Barcode Label'), | |
446 | xtype: 'proxmoxButton', | |
447 | handler: 'barcodeLabel', | |
448 | iconCls: 'fa fa-barcode', | |
449 | bind: { | |
450 | disabled: '{!changerSelected}', | |
451 | }, | |
452 | }, | |
453 | ], | |
454 | ||
455 | layout: 'auto', | |
456 | bodyPadding: 5, | |
457 | scrollable: true, | |
458 | ||
459 | items: [ | |
460 | { | |
461 | xtype: 'container', | |
462 | reference: 'content', | |
463 | layout: { | |
464 | type: 'hbox', | |
465 | aling: 'stretch', | |
466 | }, | |
467 | items: [ | |
468 | { | |
469 | xtype: 'grid', | |
470 | reference: 'slots', | |
471 | title: gettext('Slots'), | |
472 | padding: 5, | |
473 | flex: 1, | |
474 | store: { | |
475 | data: [], | |
476 | }, | |
477 | columns: [ | |
478 | { | |
479 | text: gettext('Slot'), | |
480 | dataIndex: 'entry-id', | |
481 | width: 50, | |
482 | }, | |
483 | { | |
484 | text: gettext("Content"), | |
485 | dataIndex: 'label-text', | |
486 | flex: 1, | |
487 | renderer: (value) => value || '', | |
488 | }, | |
489 | { | |
490 | text: gettext('Actions'), | |
491 | xtype: 'actioncolumn', | |
492 | width: 100, | |
493 | items: [ | |
494 | { | |
495 | iconCls: 'fa fa-rotate-90 fa-exchange', | |
496 | handler: 'slotTransfer', | |
497 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'], | |
498 | }, | |
499 | { | |
500 | iconCls: 'fa fa-rotate-90 fa-upload', | |
501 | handler: 'load', | |
502 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'], | |
503 | }, | |
504 | ], | |
505 | }, | |
506 | ], | |
507 | }, | |
508 | { | |
509 | xtype: 'container', | |
510 | flex: 2, | |
511 | defaults: { | |
512 | padding: 5, | |
513 | }, | |
514 | items: [ | |
515 | { | |
516 | xtype: 'grid', | |
517 | reference: 'drives', | |
518 | title: gettext('Drives'), | |
519 | store: { | |
520 | fields: ['entry-id', 'label-text', 'model', 'name', 'vendor', 'serial'], | |
521 | data: [], | |
522 | }, | |
523 | columns: [ | |
524 | { | |
525 | text: gettext('Slot'), | |
526 | dataIndex: 'entry-id', | |
527 | width: 50, | |
528 | }, | |
529 | { | |
530 | text: gettext("Content"), | |
531 | dataIndex: 'label-text', | |
532 | flex: 1, | |
533 | renderer: (value) => value || '', | |
534 | }, | |
535 | { | |
536 | text: gettext("Name"), | |
537 | sortable: true, | |
538 | dataIndex: 'name', | |
539 | flex: 1, | |
540 | renderer: Ext.htmlEncode, | |
541 | }, | |
542 | { | |
543 | text: gettext("Vendor"), | |
544 | sortable: true, | |
545 | dataIndex: 'vendor', | |
546 | flex: 1, | |
547 | renderer: Ext.htmlEncode, | |
548 | }, | |
549 | { | |
550 | text: gettext("Model"), | |
551 | sortable: true, | |
552 | dataIndex: 'model', | |
553 | flex: 1, | |
554 | renderer: Ext.htmlEncode, | |
555 | }, | |
556 | { | |
557 | text: gettext("Serial"), | |
558 | sortable: true, | |
559 | dataIndex: 'serial', | |
560 | flex: 1, | |
561 | renderer: Ext.htmlEncode, | |
562 | }, | |
563 | { | |
564 | xtype: 'actioncolumn', | |
565 | text: gettext('Actions'), | |
566 | width: 140, | |
567 | items: [ | |
568 | { | |
569 | iconCls: 'fa fa-rotate-270 fa-upload', | |
570 | handler: 'unload', | |
571 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'], | |
572 | }, | |
573 | { | |
574 | iconCls: 'fa fa-hdd-o', | |
575 | handler: 'cartridgeMemory', | |
576 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'], | |
577 | }, | |
578 | { | |
579 | iconCls: 'fa fa-line-chart', | |
580 | handler: 'volumeStatistics', | |
581 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'], | |
582 | }, | |
583 | { | |
584 | iconCls: 'fa fa-tag', | |
585 | handler: 'readLabel', | |
586 | isDisabled: (v, r, c, i, rec) => !rec.data['label-text'], | |
587 | }, | |
588 | { | |
589 | iconCls: 'fa fa-info-circle', | |
590 | handler: 'status', | |
591 | }, | |
592 | { | |
593 | iconCls: 'fa fa-shower', | |
594 | handler: 'cleanDrive', | |
595 | }, | |
596 | ], | |
597 | }, | |
598 | ], | |
599 | }, | |
600 | { | |
601 | xtype: 'grid', | |
602 | reference: 'import_export', | |
603 | store: { | |
604 | data: [], | |
605 | }, | |
606 | title: gettext('Import-Export'), | |
607 | columns: [ | |
608 | { | |
609 | text: gettext('Slot'), | |
610 | dataIndex: 'entry-id', | |
611 | width: 50, | |
612 | }, | |
613 | { | |
614 | text: gettext("Content"), | |
615 | dataIndex: 'label-text', | |
616 | renderer: (value) => value || '', | |
617 | flex: 1, | |
618 | }, | |
619 | { | |
620 | text: gettext('Actions'), | |
621 | items: [], | |
622 | width: 80, | |
623 | }, | |
624 | ], | |
625 | }, | |
626 | ], | |
627 | }, | |
628 | ], | |
629 | }, | |
630 | ], | |
631 | }); |