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