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