]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/node/ZFS.js
ui: node summary: rework repo status output
[pve-manager.git] / www / manager6 / node / ZFS.js
CommitLineData
fee716d3
DC
1Ext.define('PVE.node.CreateZFS', {
2 extend: 'Proxmox.window.Edit',
3 xtype: 'pveCreateZFS',
4
dc90ab40 5 onlineHelp: 'chapter_zfs',
fee716d3
DC
6 subject: 'ZFS',
7
8 showProgress: true,
dc90ab40 9 isCreate: true,
7d2e6f5b
SI
10 width: 800,
11
8058410f 12 initComponent: function() {
dc90ab40 13 let me = this;
fee716d3
DC
14
15 if (!me.nodename) {
16 throw "no node name specified";
17 }
18
dc90ab40
TL
19 let update_disklist = function() {
20 let grid = me.down('#disklist');
21 let disks = grid.getSelection();
fee716d3 22
f6710aac 23 disks.sort(function(a, b) {
dc90ab40
TL
24 let aOrder = a.get('order') || 0;
25 let bOrder = b.get('order') || 0;
26 return aOrder - bOrder;
fee716d3
DC
27 });
28
dc90ab40 29 let selectedDevices = disks.map(disk => disk.get('devpath')).join(';');
fee716d3 30
dc90ab40 31 me.down('field[name=devices]').setValue(selectedDevices);
fee716d3
DC
32 };
33
34 Ext.apply(me, {
dc90ab40 35 url: `/nodes/${me.nodename}/disks/zfs`,
fee716d3
DC
36 method: 'POST',
37 items: [
38 {
39 xtype: 'inputpanel',
dc90ab40 40 onGetValues: values => values, // FIXME leftover?
fee716d3
DC
41 column1: [
42 {
43 xtype: 'textfield',
44 hidden: true,
45 name: 'devices',
f6710aac 46 allowBlank: false,
fee716d3
DC
47 },
48 {
49 xtype: 'proxmoxtextfield',
50 name: 'name',
51 fieldLabel: gettext('Name'),
f6710aac 52 allowBlank: false,
fee716d3
DC
53 },
54 {
55 xtype: 'proxmoxcheckbox',
56 name: 'add_storage',
57 fieldLabel: gettext('Add Storage'),
f6710aac
TL
58 value: '1',
59 },
fee716d3
DC
60 ],
61 column2: [
62 {
63 xtype: 'proxmoxKVComboBox',
64 fieldLabel: gettext('RAID Level'),
65 name: 'raidlevel',
f04e415b 66 value: 'single',
fee716d3 67 comboItems: [
f04e415b 68 ['single', gettext('Single Disk')],
fee716d3
DC
69 ['mirror', 'Mirror'],
70 ['raid10', 'RAID10'],
71 ['raidz', 'RAIDZ'],
72 ['raidz2', 'RAIDZ2'],
f6710aac
TL
73 ['raidz3', 'RAIDZ3'],
74 ],
fee716d3
DC
75 },
76 {
77 xtype: 'proxmoxKVComboBox',
78 fieldLabel: gettext('Compression'),
79 name: 'compression',
80 value: 'on',
81 comboItems: [
82 ['on', 'on'],
83 ['off', 'off'],
84 ['gzip', 'gzip'],
85 ['lz4', 'lz4'],
86 ['lzjb', 'lzjb'],
f6710aac
TL
87 ['zle', 'zle'],
88 ],
fee716d3
DC
89 },
90 {
91 xtype: 'proxmoxintegerfield',
92 fieldLabel: gettext('ashift'),
93 minValue: 9,
94 maxValue: 16,
95 value: '12',
f6710aac
TL
96 name: 'ashift',
97 },
fee716d3
DC
98 ],
99 columnB: [
100 {
101 xtype: 'grid',
102 height: 200,
103 emptyText: gettext('No Disks unused'),
104 itemId: 'disklist',
105 selModel: 'checkboxmodel',
106 listeners: {
f6710aac 107 selectionchange: update_disklist,
fee716d3
DC
108 },
109 store: {
110 proxy: {
111 type: 'proxmox',
dc90ab40 112 url: `/api2/json/nodes/${me.nodename}/disks/list?type=unused`,
f6710aac 113 },
fee716d3
DC
114 },
115 columns: [
116 {
117 text: gettext('Device'),
118 dataIndex: 'devpath',
7d2e6f5b
SI
119 flex: 2,
120 },
121 {
122 text: gettext('Model'),
123 dataIndex: 'model',
124 flex: 2,
fee716d3
DC
125 },
126 {
127 text: gettext('Serial'),
7d2e6f5b
SI
128 dataIndex: 'serial',
129 flex: 2,
fee716d3
DC
130 },
131 {
132 text: gettext('Size'),
133 dataIndex: 'size',
1bd7bcdb 134 renderer: Proxmox.Utils.render_size,
7d2e6f5b 135 flex: 1,
fee716d3
DC
136 },
137 {
138 header: gettext('Order'),
139 xtype: 'widgetcolumn',
140 dataIndex: 'order',
141 sortable: true,
7d2e6f5b 142 flex: 1,
fee716d3
DC
143 widget: {
144 xtype: 'proxmoxintegerfield',
145 minValue: 1,
146 isFormField: false,
147 listeners: {
148 change: function(numberfield, value, old_value) {
dc90ab40 149 let record = numberfield.getWidgetRecord();
fee716d3
DC
150 record.set('order', value);
151 update_disklist(record);
f6710aac
TL
152 },
153 },
154 },
155 },
156 ],
157 },
158 ],
deca45c1
TL
159 },
160 {
161 xtype: 'displayfield',
162 padding: '5 0 0 0',
f71b7c28 163 userCls: 'pmx-hint',
deca45c1 164 value: 'Note: ZFS is not compatible with disks backed by a hardware ' +
dc90ab40
TL
165 'RAID controller. For details see <a target="_blank" href="' +
166 Proxmox.Utils.get_help_link('chapter_zfs') + '">the reference documentation</a>.',
f6710aac
TL
167 },
168 ],
fee716d3
DC
169 });
170
171 me.callParent();
172 me.down('#disklist').getStore().load();
f6710aac 173 },
fee716d3
DC
174});
175
e98bcf01 176Ext.define('PVE.node.ZFSDevices', {
fee716d3 177 extend: 'Ext.tree.Panel',
e98bcf01 178 xtype: 'pveZFSDevices',
dc90ab40 179
fee716d3
DC
180 stateful: true,
181 stateId: 'grid-node-zfsstatus',
182 columns: [
183 {
184 xtype: 'treecolumn',
185 text: gettext('Name'),
186 dataIndex: 'name',
f6710aac 187 flex: 1,
fee716d3
DC
188 },
189 {
190 text: gettext('Health'),
191 renderer: PVE.Utils.render_zfs_health,
f6710aac 192 dataIndex: 'state',
fee716d3 193 },
eb43c6f4 194 {
d52134b7 195 text: 'READ',
f6710aac 196 dataIndex: 'read',
eb43c6f4
TM
197 },
198 {
d52134b7 199 text: 'WRITE',
f6710aac 200 dataIndex: 'write',
eb43c6f4
TM
201 },
202 {
d52134b7 203 text: 'CKSUM',
f6710aac 204 dataIndex: 'cksum',
eb43c6f4 205 },
fee716d3
DC
206 {
207 text: gettext('Message'),
f6710aac
TL
208 dataIndex: 'msg',
209 },
fee716d3
DC
210 ],
211
fee716d3 212 reload: function() {
dc90ab40
TL
213 let me = this;
214 let sm = me.getSelectionModel();
fee716d3 215 Proxmox.Utils.API2Request({
dc90ab40 216 url: `/nodes/${me.nodename}/disks/zfs/${me.zpool}`,
fee716d3
DC
217 waitMsgTarget: me,
218 method: 'GET',
dc90ab40 219 failure: (response, opts) => Proxmox.Utils.setErrorMask(me, response.htmlStatus),
fee716d3
DC
220 success: function(response, opts) {
221 sm.deselectAll();
222 me.setRootNode(response.result.data);
223 me.expandAll();
f6710aac 224 },
fee716d3
DC
225 });
226 },
227
dc90ab40
TL
228 rootVisible: true,
229 selModel: 'treemodel',
230 fields: [
231 'name',
232 'status',
233 {
234 type: 'string',
235 name: 'iconCls',
236 calculate: data => data.leaf ? `fa x-fa-tree fa-hdd-o` : undefined,
237 },
238 ],
239
fee716d3 240 initComponent: function() {
dc90ab40 241 let me = this;
fee716d3
DC
242
243 if (!me.nodename) {
244 throw "no node name specified";
245 }
fee716d3
DC
246 if (!me.zpool) {
247 throw "no zpool specified";
248 }
249
fee716d3
DC
250 me.callParent();
251
252 me.reload();
f6710aac 253 },
fee716d3
DC
254});
255
e98bcf01
TM
256Ext.define('PVE.node.ZFSStatus', {
257 extend: 'Proxmox.grid.ObjectGrid',
258 xtype: 'pveZFSStatus',
259 layout: 'fit',
260 border: false,
261
262 initComponent: function() {
dc90ab40 263 let me = this;
e98bcf01
TM
264
265 if (!me.nodename) {
266 throw "no node name specified";
267 }
e98bcf01
TM
268 if (!me.zpool) {
269 throw "no zpool specified";
270 }
271
dc90ab40 272 me.url = `/api2/extjs/nodes/${me.nodename}/disks/zfs/${me.zpool}`;
e98bcf01
TM
273
274 me.rows = {
275 scan: {
f6710aac 276 header: gettext('Scan'),
face18ee 277 defaultValue: gettext('No Data'),
e98bcf01
TM
278 },
279 status: {
f6710aac 280 header: gettext('Status'),
e98bcf01
TM
281 },
282 action: {
f6710aac 283 header: gettext('Action'),
e98bcf01
TM
284 },
285 errors: {
f6710aac
TL
286 header: gettext('Errors'),
287 },
e98bcf01
TM
288 };
289
290 me.callParent();
291 me.reload();
f6710aac 292 },
e98bcf01
TM
293});
294
fee716d3
DC
295Ext.define('PVE.node.ZFSList', {
296 extend: 'Ext.grid.Panel',
297 xtype: 'pveZFSList',
298
299 stateful: true,
300 stateId: 'grid-node-zfs',
301 columns: [
302 {
303 text: gettext('Name'),
304 dataIndex: 'name',
f6710aac 305 flex: 1,
fee716d3
DC
306 },
307 {
308 header: gettext('Size'),
309 renderer: Proxmox.Utils.format_size,
f6710aac 310 dataIndex: 'size',
fee716d3
DC
311 },
312 {
313 header: gettext('Free'),
314 renderer: Proxmox.Utils.format_size,
f6710aac 315 dataIndex: 'free',
fee716d3
DC
316 },
317 {
318 header: gettext('Allocated'),
319 renderer: Proxmox.Utils.format_size,
f6710aac 320 dataIndex: 'alloc',
fee716d3
DC
321 },
322 {
323 header: gettext('Fragmentation'),
324 renderer: function(value) {
325 return value.toString() + '%';
326 },
f6710aac 327 dataIndex: 'frag',
fee716d3
DC
328 },
329 {
330 header: gettext('Health'),
331 renderer: PVE.Utils.render_zfs_health,
f6710aac 332 dataIndex: 'health',
fee716d3
DC
333 },
334 {
335 header: gettext('Deduplication'),
336 hidden: true,
337 renderer: function(value) {
338 return value.toFixed(2).toString() + 'x';
339 },
f6710aac
TL
340 dataIndex: 'dedup',
341 },
fee716d3
DC
342 ],
343
344 rootVisible: false,
345 useArrows: true,
346
347 tbar: [
348 {
349 text: gettext('Reload'),
350 iconCls: 'fa fa-refresh',
351 handler: function() {
dc90ab40 352 this.up('panel').reload();
f6710aac 353 },
fee716d3
DC
354 },
355 {
356 text: gettext('Create') + ': ZFS',
357 handler: function() {
dc90ab40
TL
358 let view = this.up('panel');
359 Ext.create('PVE.node.CreateZFS', {
360 nodename: view.nodename,
361 listeners: {
362 destroy: () => view.reload(),
363 },
364 autoShow: true,
365 });
f6710aac 366 },
fee716d3
DC
367 },
368 {
369 text: gettext('Detail'),
370 itemId: 'detailbtn',
371 disabled: true,
372 handler: function() {
dc90ab40
TL
373 let view = this.up('panel');
374 let selection = view.getSelection();
375 if (selection.length) {
376 view.show_detail(selection[0].get('name'));
fee716d3 377 }
f6710aac
TL
378 },
379 },
fee716d3
DC
380 ],
381
382 show_detail: function(zpool) {
dc90ab40 383 let me = this;
8cfe2088 384
dc90ab40 385 let detailsgrid = Ext.create('PVE.node.ZFSStatus', {
37e228da 386 layout: 'fit',
8cfe2088 387 nodename: me.nodename,
37e228da 388 flex: 0,
f6710aac 389 zpool: zpool,
8cfe2088 390 });
dc90ab40 391 let devicetree = Ext.create('PVE.node.ZFSDevices', {
37e228da 392 title: gettext('Devices'),
8cfe2088 393 nodename: me.nodename,
37e228da 394 flex: 1,
f6710aac 395 zpool: zpool,
8cfe2088
TM
396 });
397
dc90ab40 398 Ext.create('Ext.window.Window', {
fee716d3
DC
399 modal: true,
400 width: 800,
dc90ab40 401 height: 600,
fee716d3
DC
402 resizable: true,
403 layout: 'fit',
404 title: gettext('Status') + ': ' + zpool,
dc90ab40
TL
405 items: [
406 {
407 xtype: 'panel',
408 region: 'center',
409 layout: {
410 type: 'vbox',
411 align: 'stretch',
f6710aac 412 },
dc90ab40
TL
413 items: [
414 detailsgrid,
415 devicetree,
416 ],
417 tbar: [
418 {
419 text: gettext('Reload'),
420 iconCls: 'fa fa-refresh',
421 handler: function() {
422 devicetree.reload();
423 detailsgrid.reload();
424 },
425 },
426 ],
427 },
428 ],
429 autoShow: true,
430 });
fee716d3
DC
431 },
432
433 set_button_status: function() {
434 var me = this;
fee716d3
DC
435 },
436
437 reload: function() {
438 var me = this;
439 me.store.load();
440 me.store.sort();
441 },
442
443 listeners: {
444 activate: function() {
dc90ab40 445 this.reload();
fee716d3
DC
446 },
447 selectionchange: function() {
dc90ab40 448 this.down('#detailbtn').setDisabled(this.getSelection().length === 0);
fee716d3
DC
449 },
450 itemdblclick: function(grid, record) {
dc90ab40 451 this.show_detail(record.get('name'));
f6710aac 452 },
fee716d3
DC
453 },
454
455 initComponent: function() {
dc90ab40 456 let me = this;
fee716d3
DC
457
458 me.nodename = me.pveSelNode.data.node;
459 if (!me.nodename) {
460 throw "no node name specified";
461 }
462
463 Ext.apply(me, {
464 store: {
465 fields: ['name', 'size', 'free', 'alloc', 'dedup', 'frag', 'health'],
466 proxy: {
467 type: 'proxmox',
dc90ab40 468 url: `/api2/json/nodes/${me.nodename}/disks/zfs`,
fee716d3 469 },
f6710aac
TL
470 sorters: 'name',
471 },
fee716d3
DC
472 });
473
474 me.callParent();
475
476 Proxmox.Utils.monStoreErrors(me, me.getStore(), true);
477 me.reload();
f6710aac 478 },
fee716d3
DC
479});
480