]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/ceph/OSD.js
use Proxmox.Utils instead of PVE.Utils
[pve-manager.git] / www / manager6 / ceph / OSD.js
1 Ext.define('PVE.form.CephDiskSelector', {
2 extend: 'Proxmox.form.ComboGrid',
3 alias: ['widget.pveCephDiskSelector'],
4
5 diskType: 'journal_disks',
6
7 valueField: 'devpath',
8 displayField: 'devpath',
9 emptyText: gettext('No Disks unused'),
10 listConfig: {
11 columns: [
12 {
13 header: gettext('Device'),
14 width: 80,
15 sortable: true,
16 dataIndex: 'devpath'
17 },
18 {
19 header: gettext('Size'),
20 width: 60,
21 sortable: false,
22 renderer: Proxmox.Utils.format_size,
23 dataIndex: 'size'
24 },
25 {
26 header: gettext('Serial'),
27 flex: 1,
28 sortable: true,
29 dataIndex: 'serial'
30 }
31 ]
32 },
33 initComponent: function() {
34 var me = this;
35
36 var nodename = me.nodename;
37 if (!nodename) {
38 throw "no node name specified";
39 }
40
41 var store = Ext.create('Ext.data.Store', {
42 filterOnLoad: true,
43 model: 'ceph-disk-list',
44 proxy: {
45 type: 'pve',
46 url: "/api2/json/nodes/" + nodename + "/ceph/disks",
47 extraParams: { type: me.diskType }
48 },
49 sorters: [
50 {
51 property : 'devpath',
52 direction: 'ASC'
53 }
54 ]
55 });
56
57 Ext.apply(me, {
58 store: store
59 });
60
61 me.callParent();
62
63 store.load();
64 }
65 }, function() {
66
67 Ext.define('ceph-disk-list', {
68 extend: 'Ext.data.Model',
69 fields: [ 'devpath', 'used', { name: 'size', type: 'number'},
70 {name: 'osdid', type: 'number'},
71 'vendor', 'model', 'serial'],
72 idProperty: 'devpath'
73 });
74 });
75
76 Ext.define('PVE.CephCreateOsd', {
77 extend: 'PVE.window.Edit',
78 alias: ['widget.pveCephCreateOsd'],
79
80 subject: 'Ceph OSD',
81
82 showProgress: true,
83
84 onlineHelp: 'pve_ceph_osds',
85
86 initComponent : function() {
87 var me = this;
88
89 if (!me.nodename) {
90 throw "no node name specified";
91 }
92
93 me.isCreate = true;
94
95 Ext.applyIf(me, {
96 url: "/nodes/" + me.nodename + "/ceph/osd",
97 method: 'POST',
98 items: [
99 {
100 xtype: 'pveCephDiskSelector',
101 name: 'dev',
102 nodename: me.nodename,
103 diskType: 'unused',
104 fieldLabel: gettext('Disk'),
105 allowBlank: false
106 },
107 {
108 xtype: 'pveCephDiskSelector',
109 name: 'journal_dev',
110 nodename: me.nodename,
111 diskType: 'journal_disks',
112 fieldLabel: gettext('Journal/DB Disk'),
113 value: '',
114 autoSelect: false,
115 allowBlank: true,
116 emptyText: 'use OSD disk'
117 },
118 {
119 xtype: 'pvecheckbox',
120 name: 'bluestore',
121 fieldLabel: 'Bluestore',
122 uncheckedValue: '0',
123 value: '1'
124 }
125 ]
126 });
127
128 me.callParent();
129 }
130 });
131
132 Ext.define('PVE.CephRemoveOsd', {
133 extend: 'PVE.window.Edit',
134 alias: ['widget.pveCephRemoveOsd'],
135
136 isRemove: true,
137
138 showProgress: true,
139 method: 'DELETE',
140 items: [
141 {
142 xtype: 'pvecheckbox',
143 name: 'cleanup',
144 checked: true,
145 labelWidth: 130,
146 fieldLabel: gettext('Remove Partitions')
147 }
148 ],
149 initComponent : function() {
150
151 var me = this;
152
153 if (!me.nodename) {
154 throw "no node name specified";
155 }
156 if (me.osdid === undefined || me.osdid < 0) {
157 throw "no osdid specified";
158 }
159
160 me.isCreate = true;
161
162 me.title = gettext('Destroy') + ': Ceph OSD osd.' + me.osdid.toString();
163
164 Ext.applyIf(me, {
165 url: "/nodes/" + me.nodename + "/ceph/osd/" + me.osdid.toString()
166 });
167
168 me.callParent();
169 }
170 });
171
172 Ext.define('PVE.node.CephOsdTree', {
173 extend: 'Ext.tree.Panel',
174 alias: ['widget.pveNodeCephOsdTree'],
175 onlineHelp: 'chapter_pveceph',
176 stateful: true,
177 stateId: 'grid-ceph-osd',
178 columns: [
179 {
180 xtype: 'treecolumn',
181 text: 'Name',
182 dataIndex: 'name',
183 width: 150
184 },
185 {
186 text: 'Type',
187 dataIndex: 'type',
188 align: 'right',
189 width: 60
190 },
191 {
192 text: gettext("Class"),
193 dataIndex: 'device_class',
194 align: 'right',
195 width: 40
196 },
197 {
198 text: "OSD Type",
199 dataIndex: 'osdtype',
200 align: 'right',
201 width: 40
202 },
203 {
204 text: "Bluestore Device",
205 dataIndex: 'blfsdev',
206 align: 'right',
207 width: 40,
208 hidden: true
209 },
210 {
211 text: "DB Device",
212 dataIndex: 'dbdev',
213 align: 'right',
214 width: 40,
215 hidden: true
216 },
217 {
218 text: "WAL Device",
219 dataIndex: 'waldev',
220 align: 'right',
221 renderer: function(value, metaData, rec) {
222 if (!value &&
223 rec.data.osdtype === 'bluestore' &&
224 rec.data.type === 'osd') {
225 return 'N/A';
226 }
227 return value;
228 },
229 width: 40,
230 hidden: true
231 },
232 {
233 text: 'Status',
234 dataIndex: 'status',
235 align: 'right',
236 renderer: function(value, metaData, rec) {
237 if (!value) {
238 return value;
239 }
240 var inout = rec.data['in'] ? 'in' : 'out';
241 var updownicon = value === 'up' ? 'good fa-arrow-circle-up' :
242 'critical fa-arrow-circle-down';
243
244 var inouticon = rec.data['in'] ? 'good fa-circle' :
245 'warning fa-circle-o';
246
247 var text = value + ' <i class="fa ' + updownicon + '"></i> / ' +
248 inout + ' <i class="fa ' + inouticon + '"></i>';
249
250 return text;
251 },
252 width: 80
253 },
254 {
255 text: 'weight',
256 dataIndex: 'crush_weight',
257 align: 'right',
258 renderer: function(value, metaData, rec) {
259 if (rec.data.type !== 'osd') {
260 return '';
261 }
262 return value;
263 },
264 width: 80
265 },
266 {
267 text: 'reweight',
268 dataIndex: 'reweight',
269 align: 'right',
270 renderer: function(value, metaData, rec) {
271 if (rec.data.type !== 'osd') {
272 return '';
273 }
274 return value;
275 },
276 width: 90
277 },
278 {
279 header: gettext('Used'),
280 columns: [
281 {
282 text: '%',
283 dataIndex: 'percent_used',
284 align: 'right',
285 renderer: function(value, metaData, rec) {
286 if (rec.data.type !== 'osd') {
287 return '';
288 }
289 return Ext.util.Format.number(value, '0.00');
290 },
291 width: 80
292 },
293 {
294 text: gettext('Total'),
295 dataIndex: 'total_space',
296 align: 'right',
297 renderer: function(value, metaData, rec) {
298 if (rec.data.type !== 'osd') {
299 return '';
300 }
301 return PVE.Utils.render_size(value);
302 },
303 width: 100
304 }
305 ]
306 },
307 {
308 header: gettext('Latency (ms)'),
309 columns: [
310 {
311 text: 'Apply',
312 dataIndex: 'apply_latency_ms',
313 align: 'right',
314 renderer: function(value, metaData, rec) {
315 if (rec.data.type !== 'osd') {
316 return '';
317 }
318 return value;
319 },
320 width: 60
321 },
322 {
323 text: 'Commit',
324 dataIndex: 'commit_latency_ms',
325 align: 'right',
326 renderer: function(value, metaData, rec) {
327 if (rec.data.type !== 'osd') {
328 return '';
329 }
330 return value;
331 },
332 width: 60
333 }
334 ]
335 }
336 ],
337 initComponent: function() {
338 /*jslint confusion: true */
339 var me = this;
340
341 // we expect noout to be not set by default
342 var noout = false;
343
344 var nodename = me.pveSelNode.data.node;
345 if (!nodename) {
346 throw "no node name specified";
347 }
348
349 var sm = Ext.create('Ext.selection.TreeModel', {});
350
351 var set_button_status; // defined later
352
353 var reload = function() {
354 Proxmox.Utils.API2Request({
355 url: "/nodes/" + nodename + "/ceph/osd",
356 waitMsgTarget: me,
357 method: 'GET',
358 failure: function(response, opts) {
359 Proxmox.Utils.setErrorMask(me, response.htmlStatus);
360 },
361 success: function(response, opts) {
362 sm.deselectAll();
363 me.setRootNode(response.result.data.root);
364 me.expandAll();
365 // extract noout flag
366 if (response.result.data.flags &&
367 response.result.data.flags.search(/noout/) !== -1) {
368 noout = true;
369 } else {
370 noout = false;
371 }
372 set_button_status();
373 }
374 });
375 };
376
377 var osd_cmd = function(cmd) {
378 var rec = sm.getSelection()[0];
379 if (!(rec && (rec.data.id >= 0) && rec.data.host)) {
380 return;
381 }
382 Proxmox.Utils.API2Request({
383 url: "/nodes/" + rec.data.host + "/ceph/osd/" +
384 rec.data.id + '/' + cmd,
385 waitMsgTarget: me,
386 method: 'POST',
387 success: reload,
388 failure: function(response, opts) {
389 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
390 }
391 });
392 };
393
394 var service_cmd = function(cmd) {
395 var rec = sm.getSelection()[0];
396 if (!(rec && rec.data.name && rec.data.host)) {
397 return;
398 }
399 Proxmox.Utils.API2Request({
400 url: "/nodes/" + rec.data.host + "/ceph/" + cmd,
401 params: { service: rec.data.name },
402 waitMsgTarget: me,
403 method: 'POST',
404 success: function(response, options) {
405 var upid = response.result.data;
406 var win = Ext.create('PVE.window.TaskProgress', { upid: upid });
407 win.show();
408 me.mon(win, 'close', reload, me);
409 },
410 failure: function(response, opts) {
411 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
412 }
413 });
414 };
415
416 var create_btn = new Proxmox.button.Button({
417 text: gettext('Create') + ': OSD',
418 handler: function() {
419 var rec = sm.getSelection()[0];
420
421 var win = Ext.create('PVE.CephCreateOsd', {
422 nodename: nodename
423 });
424 win.show();
425 me.mon(win, 'close', reload, me);
426 }
427 });
428
429 var start_btn = new Ext.Button({
430 text: gettext('Start'),
431 disabled: true,
432 handler: function(){ service_cmd('start'); }
433 });
434
435 var stop_btn = new Ext.Button({
436 text: gettext('Stop'),
437 disabled: true,
438 handler: function(){ service_cmd('stop'); }
439 });
440
441 var osd_out_btn = new Ext.Button({
442 text: 'Out',
443 disabled: true,
444 handler: function(){ osd_cmd('out'); }
445 });
446
447 var osd_in_btn = new Ext.Button({
448 text: 'In',
449 disabled: true,
450 handler: function(){ osd_cmd('in'); }
451 });
452
453 var remove_btn = new Ext.Button({
454 text: gettext('Destroy'),
455 disabled: true,
456 handler: function(){
457 var rec = sm.getSelection()[0];
458 if (!(rec && (rec.data.id >= 0) && rec.data.host)) {
459 return;
460 }
461
462 var win = Ext.create('PVE.CephRemoveOsd', {
463 nodename: rec.data.host,
464 osdid: rec.data.id
465 });
466 win.show();
467 me.mon(win, 'close', reload, me);
468 }
469 });
470
471 var noout_btn = new Ext.Button({
472 text: gettext('Set noout'),
473 handler: function() {
474 Proxmox.Utils.API2Request({
475 url: "/nodes/" + nodename + "/ceph/flags/noout",
476 waitMsgTarget: me,
477 method: noout ? 'DELETE' : 'POST',
478 failure: function(response, opts) {
479 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
480 },
481 success: reload
482 });
483 }
484 });
485
486 var osd_label = new Ext.toolbar.TextItem({
487 data: {
488 osd: undefined
489 },
490 tpl: [
491 '<tpl if="osd">',
492 '{osd}:',
493 '<tpl else>',
494 gettext('No OSD selected'),
495 '</tpl>'
496 ]
497 });
498
499 set_button_status = function() {
500 var rec = sm.getSelection()[0];
501 noout_btn.setText(noout?gettext('Unset noout'):gettext('Set noout'));
502
503 if (!rec) {
504 start_btn.setDisabled(true);
505 stop_btn.setDisabled(true);
506 remove_btn.setDisabled(true);
507 osd_out_btn.setDisabled(true);
508 osd_in_btn.setDisabled(true);
509 return;
510 }
511
512 var isOsd = (rec.data.host && (rec.data.type === 'osd') && (rec.data.id >= 0));
513
514 start_btn.setDisabled(!(isOsd && (rec.data.status !== 'up')));
515 stop_btn.setDisabled(!(isOsd && (rec.data.status !== 'down')));
516 remove_btn.setDisabled(!(isOsd && (rec.data.status === 'down')));
517
518 osd_out_btn.setDisabled(!(isOsd && rec.data['in']));
519 osd_in_btn.setDisabled(!(isOsd && !rec.data['in']));
520
521 osd_label.update(isOsd?{osd:rec.data.name}:undefined);
522 };
523
524 sm.on('selectionchange', set_button_status);
525
526 var reload_btn = new Ext.Button({
527 text: gettext('Reload'),
528 handler: reload
529 });
530
531 Ext.apply(me, {
532 tbar: [ create_btn, reload_btn, noout_btn, '->', osd_label, start_btn, stop_btn, osd_out_btn, osd_in_btn, remove_btn ],
533 rootVisible: false,
534 useArrows: true,
535 fields: ['name', 'type', 'status', 'host', 'in', 'id' ,
536 { type: 'number', name: 'reweight' },
537 { type: 'number', name: 'percent_used' },
538 { type: 'integer', name: 'bytes_used' },
539 { type: 'integer', name: 'total_space' },
540 { type: 'integer', name: 'apply_latency_ms' },
541 { type: 'integer', name: 'commit_latency_ms' },
542 { type: 'string', name: 'device_class' },
543 { type: 'string', name: 'osdtype' },
544 { type: 'string', name: 'blfsdev' },
545 { type: 'string', name: 'dbdev' },
546 { type: 'string', name: 'waldev' },
547 { type: 'string', name: 'iconCls', calculate: function(data) {
548 var iconCls = 'fa x-fa-tree fa-';
549 switch (data.type) {
550 case 'host':
551 iconCls += 'building';
552 break;
553 case 'osd':
554 iconCls += 'hdd-o';
555 break;
556 case 'root':
557 iconCls += 'server';
558 break;
559 default:
560 return undefined;
561 }
562 return iconCls;
563 } },
564 { type: 'number', name: 'crush_weight' }],
565 selModel: sm,
566
567 listeners: {
568 activate: function() {
569 reload();
570 }
571 }
572 });
573
574 me.callParent();
575
576 reload();
577 }
578 });