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