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