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