]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/node/ZFS.js
bump version to 8.2.6
[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: get => get('raidLevel')?.startsWith("draid"),
18 },
19 },
20
21 initComponent: function() {
22 let me = this;
23
24 if (!me.nodename) {
25 throw "no node name specified";
26 }
27
28 Ext.apply(me, {
29 url: `/nodes/${me.nodename}/disks/zfs`,
30 method: 'POST',
31 items: [
32 {
33 xtype: 'inputpanel',
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 },
43 column1: [
44 {
45 xtype: 'proxmoxtextfield',
46 name: 'name',
47 fieldLabel: gettext('Name'),
48 allowBlank: false,
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 },
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 ['draid', 'dRAID'],
82 ['draid2', 'dRAID2'],
83 ['draid3', 'dRAID3'],
84 ],
85 bind: {
86 value: '{raidLevel}',
87 },
88 },
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'],
100 ['zle', 'zle'],
101 ['zstd', 'zstd'],
102 ],
103 },
104 {
105 xtype: 'proxmoxintegerfield',
106 fieldLabel: gettext('ashift'),
107 minValue: 9,
108 maxValue: 16,
109 value: '12',
110 name: 'ashift',
111 },
112 ],
113 columnB: [
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 },
156 {
157 xtype: 'pmxMultiDiskSelector',
158 name: 'devices',
159 nodename: me.nodename,
160 diskType: 'unused',
161 includePartitions: true,
162 height: 200,
163 emptyText: gettext('No Disks unused'),
164 itemId: 'disklist',
165 },
166 ],
167 },
168 {
169 xtype: 'displayfield',
170 padding: '5 0 0 0',
171 userCls: 'pmx-hint',
172 value: 'Note: ZFS is not compatible with disks backed by a hardware ' +
173 'RAID controller. For details see <a target="_blank" href="' +
174 Proxmox.Utils.get_help_link('chapter_zfs') + '">the reference documentation</a>.',
175 },
176 ],
177 });
178
179 me.callParent();
180 },
181
182 });
183
184 Ext.define('PVE.node.ZFSList', {
185 extend: 'Ext.grid.Panel',
186 xtype: 'pveZFSList',
187
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
212 Ext.create('PVE.window.SafeDestroyStorage', {
213 url: `/nodes/${view.nodename}/disks/zfs/${pool}`,
214 item: { id: pool },
215 taskName: 'zfsremove',
216 taskDone: () => { view.reload(); },
217 }).show();
218 },
219 },
220
221 stateful: true,
222 stateId: 'grid-node-zfs',
223 columns: [
224 {
225 text: gettext('Name'),
226 dataIndex: 'name',
227 flex: 1,
228 },
229 {
230 header: gettext('Size'),
231 renderer: Proxmox.Utils.format_size,
232 dataIndex: 'size',
233 },
234 {
235 header: gettext('Free'),
236 renderer: Proxmox.Utils.format_size,
237 dataIndex: 'free',
238 },
239 {
240 header: gettext('Allocated'),
241 renderer: Proxmox.Utils.format_size,
242 dataIndex: 'alloc',
243 },
244 {
245 header: gettext('Fragmentation'),
246 renderer: function(value) {
247 return value.toString() + '%';
248 },
249 dataIndex: 'frag',
250 },
251 {
252 header: gettext('Health'),
253 renderer: PVE.Utils.render_zfs_health,
254 dataIndex: 'health',
255 },
256 {
257 header: gettext('Deduplication'),
258 hidden: true,
259 renderer: function(value) {
260 return value.toFixed(2).toString() + 'x';
261 },
262 dataIndex: 'dedup',
263 },
264 ],
265
266 rootVisible: false,
267 useArrows: true,
268
269 tbar: [
270 {
271 text: gettext('Reload'),
272 iconCls: 'fa fa-refresh',
273 handler: function() {
274 this.up('panel').reload();
275 },
276 },
277 {
278 text: gettext('Create') + ': ZFS',
279 handler: function() {
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 });
288 },
289 },
290 {
291 text: gettext('Detail'),
292 itemId: 'detailbtn',
293 disabled: true,
294 handler: function() {
295 let view = this.up('panel');
296 let selection = view.getSelection();
297 if (selection.length) {
298 view.show_detail(selection[0].get('name'));
299 }
300 },
301 },
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 },
341 ],
342
343 show_detail: function(zpool) {
344 let me = this;
345
346 Ext.create('Proxmox.window.ZFSDetail', {
347 zpool,
348 nodename: me.nodename,
349 }).show();
350 },
351
352 set_button_status: function() {
353 var me = this;
354 },
355
356 reload: function() {
357 var me = this;
358 me.store.load();
359 me.store.sort();
360 },
361
362 listeners: {
363 activate: function() {
364 this.reload();
365 },
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 || '');
372 },
373 itemdblclick: function(grid, record) {
374 this.show_detail(record.get('name'));
375 },
376 },
377
378 initComponent: function() {
379 let me = this;
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',
391 url: `/api2/json/nodes/${me.nodename}/disks/zfs`,
392 },
393 sorters: 'name',
394 },
395 });
396
397 me.callParent();
398
399 Proxmox.Utils.monStoreErrors(me, me.getStore(), true);
400 me.reload();
401 },
402 });
403