]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/ceph/Pool.js
ui: ceph status: fix code style, xtype must come first
[pve-manager.git] / www / manager6 / ceph / Pool.js
CommitLineData
4618a869
AA
1Ext.define('PVE.CephPoolInputPanel', {
2 extend: 'Proxmox.panel.InputPanel',
3 xtype: 'pveCephPoolInputPanel',
4 mixins: ['Proxmox.Mixin.CBind'],
024be9c7 5
7244f4bf 6 showProgress: true,
35085f4a 7 onlineHelp: 'pve_ceph_pools',
7244f4bf 8
024be9c7 9 subject: 'Ceph Pool',
4618a869 10 column1: [
c449c89a 11 {
4618a869 12 xtype: 'pmxDisplayEditField',
c449c89a 13 fieldLabel: gettext('Name'),
4618a869
AA
14 cbind: {
15 editable: '{isCreate}',
16 value: '{pool_name}',
4618a869 17 },
c449c89a 18 name: 'name',
f6710aac 19 allowBlank: false,
c449c89a
DC
20 },
21 {
1a042025
AL
22 xtype: 'pmxDisplayEditField',
23 cbind: {
24 editable: '{!isErasure}',
25 },
c449c89a
DC
26 fieldLabel: gettext('Size'),
27 name: 'size',
1a042025
AL
28 editConfig: {
29 xtype: 'proxmoxintegerfield',
30 value: 3,
31 minValue: 2,
32 maxValue: 7,
33 allowBlank: false,
34 listeners: {
35 change: function(field, val) {
36 let size = Math.round(val / 2);
37 if (size > 1) {
38 field.up('inputpanel').down('field[name=min_size]').setValue(size);
39 }
40 },
4618a869
AA
41 },
42 },
1a042025 43
4618a869
AA
44 },
45 ],
46 column2: [
47 {
48 xtype: 'proxmoxKVComboBox',
49 fieldLabel: 'PG Autoscale Mode',
50 name: 'pg_autoscale_mode',
51 comboItems: [
52 ['warn', 'warn'],
53 ['on', 'on'],
54 ['off', 'off'],
55 ],
5da1b6d0 56 value: 'on', // FIXME: check ceph version and only default to on on octopus and newer
4618a869
AA
57 allowBlank: false,
58 autoSelect: false,
59 labelWidth: 140,
c449c89a 60 },
4618a869
AA
61 {
62 xtype: 'proxmoxcheckbox',
63 fieldLabel: gettext('Add as Storage'),
64 cbind: {
65 value: '{isCreate}',
66 hidden: '{!isCreate}',
67 },
68 name: 'add_storages',
69 labelWidth: 140,
70 autoEl: {
71 tag: 'div',
72 'data-qtip': gettext('Add the new pool to the cluster storage configuration.'),
73 },
74 },
75 ],
76 advancedColumn1: [
c449c89a 77 {
bf96f60d 78 xtype: 'proxmoxintegerfield',
c449c89a
DC
79 fieldLabel: gettext('Min. Size'),
80 name: 'min_size',
f441a266 81 value: 2,
4618a869
AA
82 cbind: {
83 minValue: (get) => get('isCreate') ? 2 : 1,
84 },
eef1979c 85 maxValue: 7,
f6710aac 86 allowBlank: false,
4618a869 87 listeners: {
00c0fc4b
TL
88 change: function(field, minSize) {
89 let panel = field.up('inputpanel');
90 let size = panel.down('field[name=size]').getValue();
4618a869 91
7748b41f 92 let showWarning = minSize < (size / 2) && minSize !== size;
4618a869 93
00c0fc4b
TL
94 let fieldLabel = gettext('Min. Size');
95 if (showWarning) {
96 fieldLabel = gettext('Min. Size') + ' <i class="fa fa-exclamation-triangle warning"></i>';
97 }
98 panel.down('field[name=min_size-warning]').setHidden(!showWarning);
99 field.setFieldLabel(fieldLabel);
4618a869
AA
100 },
101 },
102 },
103 {
104 xtype: 'displayfield',
105 name: 'min_size-warning',
106 userCls: 'pmx-hint',
7748b41f 107 value: gettext('min_size < size/2 can lead to data loss, incomplete PGs or unfound objects.'),
4618a869 108 hidden: true,
c449c89a
DC
109 },
110 {
1a042025 111 xtype: 'pmxDisplayEditField',
cd99757b 112 cbind: {
1a042025 113 editable: '{!isErasure}',
cd99757b
TL
114 nodename: '{nodename}',
115 isCreate: '{isCreate}',
116 },
1a042025
AL
117 fieldLabel: 'Crush Rule', // do not localize
118 name: 'crush_rule',
119 editConfig: {
120 xtype: 'pveCephRuleSelector',
121 allowBlank: false,
122 },
c449c89a
DC
123 },
124 {
bf96f60d 125 xtype: 'proxmoxintegerfield',
4618a869 126 fieldLabel: '# of PGs',
c449c89a 127 name: 'pg_num',
6ad70a2b 128 value: 128,
4618a869 129 minValue: 1,
c449c89a 130 maxValue: 32768,
4618a869
AA
131 allowBlank: false,
132 emptyText: 128,
133 },
134 ],
135 advancedColumn2: [
136 {
137 xtype: 'numberfield',
4d7acd65 138 fieldLabel: gettext('Target Ratio'),
4618a869 139 name: 'target_size_ratio',
4618a869
AA
140 minValue: 0,
141 decimalPrecision: 3,
23b316a3 142 allowBlank: true,
4618a869 143 emptyText: '0.0',
4d7acd65
TL
144 autoEl: {
145 tag: 'div',
146 'data-qtip': gettext('The ratio of storage amount this pool will consume compared to other pools with ratios. Used for auto-scaling.'),
147 },
5947248f
DC
148 },
149 {
18a5845e 150 xtype: 'pveSizeField',
4618a869 151 name: 'target_size',
4d7acd65 152 fieldLabel: gettext('Target Size'),
18a5845e 153 unit: 'GiB',
4618a869
AA
154 minValue: 0,
155 allowBlank: true,
86a5da83 156 allowZero: true,
4618a869 157 emptyText: '0',
d7eb3e22 158 emptyValue: 0,
4d7acd65
TL
159 autoEl: {
160 tag: 'div',
161 'data-qtip': gettext('The amount of data eventually stored in this pool. Used for auto-scaling.'),
162 },
4618a869
AA
163 },
164 {
165 xtype: 'displayfield',
166 userCls: 'pmx-hint',
4d7acd65 167 value: Ext.String.format(gettext('{0} takes precedence.'), gettext('Target Ratio')), // FIXME: tooltip?
f6710aac 168 },
ae793232
AA
169 {
170 xtype: 'proxmoxintegerfield',
171 fieldLabel: 'Min. # of PGs',
172 name: 'pg_num_min',
ae793232
AA
173 minValue: 0,
174 allowBlank: true,
175 emptyText: '0',
176 },
c449c89a 177 ],
024be9c7 178
4618a869
AA
179 onGetValues: function(values) {
180 Object.keys(values || {}).forEach(function(name) {
181 if (values[name] === '') {
182 delete values[name];
183 }
184 });
185
4618a869
AA
186 return values;
187 },
4618a869
AA
188});
189
519d9f3f 190Ext.define('PVE.Ceph.PoolEdit', {
4618a869
AA
191 extend: 'Proxmox.window.Edit',
192 alias: 'widget.pveCephPoolEdit',
4618a869
AA
193 mixins: ['Proxmox.Mixin.CBind'],
194
195 cbindData: {
196 pool_name: '',
197 isCreate: (cfg) => !cfg.pool_name,
198 },
199
200 cbind: {
201 autoLoad: get => !get('isCreate'),
202 url: get => get('isCreate')
203 ? `/nodes/${get('nodename')}/ceph/pools`
204 : `/nodes/${get('nodename')}/ceph/pools/${get('pool_name')}`,
205 method: get => get('isCreate') ? 'POST' : 'PUT',
206 },
207
cc0734fc
DC
208 showProgress: true,
209
4618a869
AA
210 subject: gettext('Ceph Pool'),
211
212 items: [{
213 xtype: 'pveCephPoolInputPanel',
214 cbind: {
215 nodename: '{nodename}',
216 pool_name: '{pool_name}',
1a042025 217 isErasure: '{isErasure}',
4618a869
AA
218 isCreate: '{isCreate}',
219 },
220 }],
024be9c7
DM
221});
222
519d9f3f 223Ext.define('PVE.node.Ceph.PoolList', {
024be9c7 224 extend: 'Ext.grid.GridPanel',
9ad28182 225 alias: 'widget.pveNodeCephPoolList',
024be9c7 226
ba93a9c6 227 onlineHelp: 'chapter_pveceph',
5b7b4b76 228
361aafd0
DC
229 stateful: true,
230 stateId: 'grid-ceph-pools',
c449c89a 231 bufferedRenderer: false,
5b7b4b76 232
8058410f 233 features: [{ ftype: 'summary' }],
5b7b4b76 234
c449c89a
DC
235 columns: [
236 {
11ce9e1a
AA
237 text: gettext('Name'),
238 minWidth: 120,
239 flex: 2,
c449c89a 240 sortable: true,
f6710aac 241 dataIndex: 'pool_name',
c449c89a 242 },
a73f844d
AL
243 {
244 text: gettext('Type'),
245 minWidth: 100,
246 flex: 1,
247 dataIndex: 'type',
248 hidden: true,
249 },
c449c89a 250 {
11ce9e1a
AA
251 text: gettext('Size') + '/min',
252 minWidth: 100,
253 flex: 1,
5b7b4b76 254 align: 'right',
1bd7bcdb 255 renderer: (v, meta, rec) => `${v}/${rec.data.min_size}`,
f6710aac 256 dataIndex: 'size',
c449c89a
DC
257 },
258 {
11ce9e1a
AA
259 text: '# of Placement Groups',
260 flex: 1,
0e0f861a 261 minWidth: 100,
11ce9e1a
AA
262 align: 'right',
263 dataIndex: 'pg_num',
c449c89a
DC
264 },
265 {
11ce9e1a
AA
266 text: gettext('Optimal # of PGs'),
267 flex: 1,
0e0f861a 268 minWidth: 100,
11ce9e1a
AA
269 align: 'right',
270 dataIndex: 'pg_num_final',
271 renderer: function(value, metaData) {
272 if (!value) {
273 value = '<i class="fa fa-info-circle faded"></i> n/a';
274 metaData.tdAttr = 'data-qtip="Needs pg_autoscaler module enabled."';
275 }
276 return value;
277 },
c9508b5d 278 },
ae793232
AA
279 {
280 text: gettext('Min. # of PGs'),
281 flex: 1,
0e0f861a 282 minWidth: 100,
ae793232
AA
283 align: 'right',
284 dataIndex: 'pg_num_min',
285 hidden: true,
286 },
c449c89a 287 {
0e0f861a 288 text: gettext('Target Ratio'),
11ce9e1a 289 flex: 1,
0e0f861a 290 minWidth: 100,
11ce9e1a
AA
291 align: 'right',
292 dataIndex: 'target_size_ratio',
293 renderer: Ext.util.Format.numberRenderer('0.0000'),
294 hidden: true,
295 },
296 {
297 text: gettext('Target Size'),
298 flex: 1,
0e0f861a 299 minWidth: 100,
11ce9e1a
AA
300 align: 'right',
301 dataIndex: 'target_size',
302 hidden: true,
303 renderer: function(v, metaData, rec) {
ad33cfd5 304 let value = Proxmox.Utils.render_size(v);
11ce9e1a
AA
305 if (rec.data.target_size_ratio > 0) {
306 value = '<i class="fa fa-info-circle faded"></i> ' + value;
307 metaData.tdAttr = 'data-qtip="Target Size Ratio takes precedence over Target Size."';
308 }
309 return value;
310 },
311 },
312 {
313 text: gettext('Autoscale Mode'),
314 flex: 1,
0e0f861a 315 minWidth: 100,
11ce9e1a
AA
316 align: 'right',
317 dataIndex: 'pg_autoscale_mode',
318 },
319 {
320 text: 'CRUSH Rule (ID)',
321 flex: 1,
322 align: 'right',
323 minWidth: 150,
0e0f861a 324 renderer: (v, meta, rec) => `${v} (${rec.data.crush_rule})`,
11ce9e1a
AA
325 dataIndex: 'crush_rule_name',
326 },
327 {
328 text: gettext('Used') + ' (%)',
329 flex: 1,
0e0f861a 330 minWidth: 150,
11ce9e1a
AA
331 sortable: true,
332 align: 'right',
333 dataIndex: 'bytes_used',
334 summaryType: 'sum',
1bd7bcdb 335 summaryRenderer: Proxmox.Utils.render_size,
11ce9e1a
AA
336 renderer: function(v, meta, rec) {
337 let percentage = Ext.util.Format.percent(rec.data.percent_used, '0.00');
1bd7bcdb
DC
338 let used = Proxmox.Utils.render_size(v);
339 return `${used} (${percentage})`;
11ce9e1a 340 },
f6710aac 341 },
c449c89a 342 ],
024be9c7
DM
343 initComponent: function() {
344 var me = this;
345
346 var nodename = me.pveSelNode.data.node;
347 if (!nodename) {
348 throw "no node name specified";
349 }
350
351 var sm = Ext.create('Ext.selection.RowModel', {});
352
0c7c0d6b 353 var rstore = Ext.create('Proxmox.data.UpdateStore', {
024be9c7 354 interval: 3000,
d5066d84 355 storeid: 'ceph-pool-list' + nodename,
024be9c7
DM
356 model: 'ceph-pool-list',
357 proxy: {
261bb633
TL
358 type: 'proxmox',
359 url: `/api2/json/nodes/${nodename}/ceph/pools`,
f6710aac 360 },
024be9c7 361 });
ec455ed0
TL
362 let store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore });
363
13786fb0
TL
364 // manages the "install ceph?" overlay
365 PVE.Utils.monitor_ceph_installed(me, rstore, nodename);
024be9c7 366
4618a869 367 var run_editor = function() {
ec455ed0
TL
368 let rec = sm.getSelection()[0];
369 if (!rec || !rec.data.pool_name) {
4618a869
AA
370 return;
371 }
519d9f3f 372 Ext.create('PVE.Ceph.PoolEdit', {
4618a869
AA
373 title: gettext('Edit') + ': Ceph Pool',
374 nodename: nodename,
375 pool_name: rec.data.pool_name,
1a042025 376 isErasure: rec.data.type === 'erasure',
ec455ed0
TL
377 autoShow: true,
378 listeners: {
379 destroy: () => rstore.load(),
380 },
4618a869 381 });
4618a869
AA
382 };
383
024be9c7
DM
384 Ext.apply(me, {
385 store: store,
386 selModel: sm,
ec455ed0
TL
387 tbar: [
388 {
389 text: gettext('Create'),
390 handler: function() {
519d9f3f 391 Ext.create('PVE.Ceph.PoolEdit', {
ec455ed0
TL
392 title: gettext('Create') + ': Ceph Pool',
393 isCreate: true,
1a042025 394 isErasure: false,
ec455ed0
TL
395 nodename: nodename,
396 autoShow: true,
397 listeners: {
398 destroy: () => rstore.load(),
399 },
400 });
401 },
402 },
403 {
404 xtype: 'proxmoxButton',
405 text: gettext('Edit'),
406 selModel: sm,
407 disabled: true,
408 handler: run_editor,
409 },
410 {
411 xtype: 'proxmoxButton',
412 text: gettext('Destroy'),
413 selModel: sm,
414 disabled: true,
415 handler: function() {
416 let rec = sm.getSelection()[0];
417 if (!rec || !rec.data.pool_name) {
418 return;
419 }
420 let poolName = rec.data.pool_name;
b9635c9b 421 Ext.create('Proxmox.window.SafeDestroy', {
ec455ed0
TL
422 showProgress: true,
423 url: `/nodes/${nodename}/ceph/pools/${poolName}`,
424 params: {
425 remove_storages: 1,
426 },
427 item: {
428 type: 'CephPool',
429 id: poolName,
430 },
b9635c9b 431 taskName: 'cephdestroypool',
ec455ed0
TL
432 autoShow: true,
433 listeners: {
434 destroy: () => rstore.load(),
435 },
436 });
437 },
438 },
439 ],
024be9c7 440 listeners: {
6386068d
TL
441 activate: () => rstore.startUpdate(),
442 destroy: () => rstore.stopUpdate(),
4618a869 443 itemdblclick: run_editor,
f6710aac 444 },
024be9c7
DM
445 });
446
447 me.callParent();
f6710aac 448 },
024be9c7 449}, function() {
024be9c7
DM
450 Ext.define('ceph-pool-list', {
451 extend: 'Ext.data.Model',
8058410f
TL
452 fields: ['pool_name',
453 { name: 'pool', type: 'integer' },
454 { name: 'size', type: 'integer' },
455 { name: 'min_size', type: 'integer' },
456 { name: 'pg_num', type: 'integer' },
ae793232 457 { name: 'pg_num_min', type: 'integer' },
8058410f
TL
458 { name: 'bytes_used', type: 'integer' },
459 { name: 'percent_used', type: 'number' },
460 { name: 'crush_rule', type: 'integer' },
461 { name: 'crush_rule_name', type: 'string' },
4618a869
AA
462 { name: 'pg_autoscale_mode', type: 'string' },
463 { name: 'pg_num_final', type: 'integer' },
464 { name: 'target_size_ratio', type: 'number' },
465 { name: 'target_size', type: 'integer' },
024be9c7 466 ],
f6710aac 467 idProperty: 'pool_name',
024be9c7
DM
468 });
469});
d2692b86
DC
470
471Ext.define('PVE.form.CephRuleSelector', {
472 extend: 'Ext.form.field.ComboBox',
473 alias: 'widget.pveCephRuleSelector',
474
475 allowBlank: false,
476 valueField: 'name',
477 displayField: 'name',
478 editable: false,
479 queryMode: 'local',
480
481 initComponent: function() {
cd99757b 482 let me = this;
d2692b86
DC
483
484 if (!me.nodename) {
485 throw "no nodename given";
486 }
d2692b86 487 Ext.apply(me, {
261bb633
TL
488 store: {
489 fields: ['name'],
490 sorters: 'name',
491 proxy: {
492 type: 'proxmox',
493 url: `/api2/json/nodes/${me.nodename}/ceph/rules`,
494 },
495 autoLoad: me.isCreate ? {
496 callback: (records, op, success) => {
497 if (success && records.length > 0) {
498 me.select(records[0]);
499 }
500 },
501 } : true,
502 },
d2692b86
DC
503 });
504
505 me.callParent();
f6710aac 506 },
d2692b86
DC
507
508});