]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/node/ZFS.js
fix #3967: enable ZFS dRAID creation in WebGUI
[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 viewModel: {
13 data: {
14 raidLevel: 'single',
15 },
16 formulas: {
17 isdraid: function(get) {
18 return get('raidLevel').startsWith("draid");
19 },
20 draidconfig: function(get) {
21 if (get('isdraid')) {
22 var config = {};
23 config.data = get('draiddata');
24 config.spares = get('draidspares');
25 return PVE.Parser.printPropertyString(config);
26 } else {
27 return "";
28 }
29 },
30 },
31 },
32
33 initComponent: function() {
34 let me = this;
35
36 if (!me.nodename) {
37 throw "no node name specified";
38 }
39
40 Ext.apply(me, {
41 url: `/nodes/${me.nodename}/disks/zfs`,
42 method: 'POST',
43 items: [
44 {
45 xtype: 'inputpanel',
46 column1: [
47 {
48 xtype: 'proxmoxtextfield',
49 name: 'name',
50 fieldLabel: gettext('Name'),
51 allowBlank: false,
52 },
53 {
54 xtype: 'proxmoxcheckbox',
55 name: 'add_storage',
56 fieldLabel: gettext('Add Storage'),
57 value: '1',
58 },
59 ],
60 column2: [
61 {
62 xtype: 'proxmoxKVComboBox',
63 fieldLabel: gettext('RAID Level'),
64 name: 'raidlevel',
65 value: 'single',
66 comboItems: [
67 ['single', gettext('Single Disk')],
68 ['mirror', 'Mirror'],
69 ['raid10', 'RAID10'],
70 ['raidz', 'RAIDZ'],
71 ['raidz2', 'RAIDZ2'],
72 ['raidz3', 'RAIDZ3'],
73 ['draid', 'dRAID'],
74 ['draid2', 'dRAID2'],
75 ['draid3', 'dRAID3'],
76 ],
77 bind: {
78 value: '{raidLevel}',
79 },
80 },
81 {
82 xtype: 'proxmoxtextfield',
83 name: 'draid-config',
84 hidden: true,
85 submitValue: true,
86 bind: {
87 value: '{draidconfig}',
88 },
89 },
90 {
91 xtype: 'proxmoxintegerfield',
92 fieldLabel: gettext('DRAID data devices'),
93 minValue: 1,
94 allowBlank: false,
95 disabled: true,
96 hidden: true,
97 submitValue: false,
98 bind: {
99 disabled: '{!isdraid}',
100 hidden: '{!isdraid}',
101 value: '{draiddata}',
102 },
103 reference: 'draiddata',
104 name: 'draiddata',
105 },
106 {
107 xtype: 'proxmoxintegerfield',
108 fieldLabel: gettext('DRAID spares'),
109 minValue: 0,
110 allowBlank: false,
111 disabled: true,
112 hidden: true,
113 submitValue: false,
114 bind: {
115 disabled: '{!isdraid}',
116 hidden: '{!isdraid}',
117 value: '{draidspares}',
118 },
119 reference: 'draidspares',
120 name: 'draidspares',
121 },
122 {
123 xtype: 'proxmoxKVComboBox',
124 fieldLabel: gettext('Compression'),
125 name: 'compression',
126 value: 'on',
127 comboItems: [
128 ['on', 'on'],
129 ['off', 'off'],
130 ['gzip', 'gzip'],
131 ['lz4', 'lz4'],
132 ['lzjb', 'lzjb'],
133 ['zle', 'zle'],
134 ['zstd', 'zstd'],
135 ],
136 },
137 {
138 xtype: 'proxmoxintegerfield',
139 fieldLabel: gettext('ashift'),
140 minValue: 9,
141 maxValue: 16,
142 value: '12',
143 name: 'ashift',
144 },
145 ],
146 columnB: [
147 {
148 xtype: 'pmxMultiDiskSelector',
149 name: 'devices',
150 nodename: me.nodename,
151 diskType: 'unused',
152 includePartitions: true,
153 height: 200,
154 emptyText: gettext('No Disks unused'),
155 itemId: 'disklist',
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 <a target="_blank" href="' +
165 Proxmox.Utils.get_help_link('chapter_zfs') + '">the reference documentation</a>.',
166 },
167 ],
168 });
169
170 me.callParent();
171 },
172
173 });
174
175 Ext.define('PVE.node.ZFSList', {
176 extend: 'Ext.grid.Panel',
177 xtype: 'pveZFSList',
178
179 viewModel: {
180 data: {
181 pool: '',
182 },
183 },
184
185 controller: {
186 xclass: 'Ext.app.ViewController',
187
188 destroyPool: function() {
189 let me = this;
190 let vm = me.getViewModel();
191 let view = me.getView();
192
193 const pool = vm.get('pool');
194
195 if (!view.nodename) {
196 throw "no node name specified";
197 }
198
199 if (!pool) {
200 throw "no pool specified";
201 }
202
203 Ext.create('PVE.window.SafeDestroyStorage', {
204 url: `/nodes/${view.nodename}/disks/zfs/${pool}`,
205 item: { id: pool },
206 taskName: 'zfsremove',
207 taskDone: () => { view.reload(); },
208 }).show();
209 },
210 },
211
212 stateful: true,
213 stateId: 'grid-node-zfs',
214 columns: [
215 {
216 text: gettext('Name'),
217 dataIndex: 'name',
218 flex: 1,
219 },
220 {
221 header: gettext('Size'),
222 renderer: Proxmox.Utils.format_size,
223 dataIndex: 'size',
224 },
225 {
226 header: gettext('Free'),
227 renderer: Proxmox.Utils.format_size,
228 dataIndex: 'free',
229 },
230 {
231 header: gettext('Allocated'),
232 renderer: Proxmox.Utils.format_size,
233 dataIndex: 'alloc',
234 },
235 {
236 header: gettext('Fragmentation'),
237 renderer: function(value) {
238 return value.toString() + '%';
239 },
240 dataIndex: 'frag',
241 },
242 {
243 header: gettext('Health'),
244 renderer: PVE.Utils.render_zfs_health,
245 dataIndex: 'health',
246 },
247 {
248 header: gettext('Deduplication'),
249 hidden: true,
250 renderer: function(value) {
251 return value.toFixed(2).toString() + 'x';
252 },
253 dataIndex: 'dedup',
254 },
255 ],
256
257 rootVisible: false,
258 useArrows: true,
259
260 tbar: [
261 {
262 text: gettext('Reload'),
263 iconCls: 'fa fa-refresh',
264 handler: function() {
265 this.up('panel').reload();
266 },
267 },
268 {
269 text: gettext('Create') + ': ZFS',
270 handler: function() {
271 let view = this.up('panel');
272 Ext.create('PVE.node.CreateZFS', {
273 nodename: view.nodename,
274 listeners: {
275 destroy: () => view.reload(),
276 },
277 autoShow: true,
278 });
279 },
280 },
281 {
282 text: gettext('Detail'),
283 itemId: 'detailbtn',
284 disabled: true,
285 handler: function() {
286 let view = this.up('panel');
287 let selection = view.getSelection();
288 if (selection.length) {
289 view.show_detail(selection[0].get('name'));
290 }
291 },
292 },
293 '->',
294 {
295 xtype: 'tbtext',
296 data: {
297 pool: undefined,
298 },
299 bind: {
300 data: {
301 pool: "{pool}",
302 },
303 },
304 tpl: [
305 '<tpl if="pool">',
306 'Pool {pool}:',
307 '<tpl else>',
308 Ext.String.format(gettext('No {0} selected'), 'pool'),
309 '</tpl>',
310 ],
311 },
312 {
313 text: gettext('More'),
314 iconCls: 'fa fa-bars',
315 disabled: true,
316 bind: {
317 disabled: '{!pool}',
318 },
319 menu: [
320 {
321 text: gettext('Destroy'),
322 itemId: 'remove',
323 iconCls: 'fa fa-fw fa-trash-o',
324 handler: 'destroyPool',
325 disabled: true,
326 bind: {
327 disabled: '{!pool}',
328 },
329 },
330 ],
331 },
332 ],
333
334 show_detail: function(zpool) {
335 let me = this;
336
337 Ext.create('Proxmox.window.ZFSDetail', {
338 zpool,
339 nodename: me.nodename,
340 }).show();
341 },
342
343 set_button_status: function() {
344 var me = this;
345 },
346
347 reload: function() {
348 var me = this;
349 me.store.load();
350 me.store.sort();
351 },
352
353 listeners: {
354 activate: function() {
355 this.reload();
356 },
357 selectionchange: function(model, selected) {
358 let me = this;
359 let vm = me.getViewModel();
360
361 me.down('#detailbtn').setDisabled(selected.length === 0);
362 vm.set('pool', selected[0]?.data.name || '');
363 },
364 itemdblclick: function(grid, record) {
365 this.show_detail(record.get('name'));
366 },
367 },
368
369 initComponent: function() {
370 let me = this;
371
372 me.nodename = me.pveSelNode.data.node;
373 if (!me.nodename) {
374 throw "no node name specified";
375 }
376
377 Ext.apply(me, {
378 store: {
379 fields: ['name', 'size', 'free', 'alloc', 'dedup', 'frag', 'health'],
380 proxy: {
381 type: 'proxmox',
382 url: `/api2/json/nodes/${me.nodename}/disks/zfs`,
383 },
384 sorters: 'name',
385 },
386 });
387
388 me.callParent();
389
390 Proxmox.Utils.monStoreErrors(me, me.getStore(), true);
391 me.reload();
392 },
393 });
394