]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/ceph/ServiceList.js
update shipped appliance info index
[pve-manager.git] / www / manager6 / ceph / ServiceList.js
CommitLineData
a6c60aed
DC
1Ext.define('PVE.CephCreateService', {
2 extend: 'Proxmox.window.Edit',
2d6c2886 3 mixins: ['Proxmox.Mixin.CBind'],
a6c60aed
DC
4 xtype: 'pveCephCreateService',
5
2d6c2886
TL
6 method: 'POST',
7 isCreate: true,
a6c60aed 8 showProgress: true,
2d6c2886 9 width: 450,
a6c60aed 10
2d6c2886
TL
11 setNode: function(node) {
12 let me = this;
13 me.nodename = node;
14 me.updateUrl();
15 },
16 setExtraID: function(extraID) {
fb792310 17 let me = this;
2d6c2886
TL
18 me.extraID = me.type === 'mds' ? `-${extraID}` : '';
19 me.updateUrl();
a6c60aed 20 },
2d6c2886
TL
21 updateUrl: function() {
22 let me = this;
a6c60aed 23
2d6c2886
TL
24 let extraID = me.extraID ?? '';
25 let node = me.nodename;
a6c60aed 26
2d6c2886
TL
27 me.url = `/nodes/${node}/ceph/${me.type}/${node}${extraID}`;
28 },
29
30 defaults: {
31 labelWidth: 75,
32 },
a6c60aed
DC
33 items: [
34 {
35 xtype: 'pveNodeSelector',
a6c60aed
DC
36 fieldLabel: gettext('Host'),
37 selectCurNode: true,
38 allowBlank: false,
2d6c2886 39 submitValue: false,
a6c60aed
DC
40 listeners: {
41 change: function(f, value) {
fb792310
TL
42 let view = this.up('pveCephCreateService');
43 view.setNode(value);
f6710aac
TL
44 },
45 },
46 },
2d6c2886
TL
47 {
48 xtype: 'textfield',
49 fieldLabel: gettext('Extra ID'),
50 regex: /[a-zA-Z0-9]+/,
51 regexText: gettext('ID may only consist of alphanumeric characters'),
52 submitValue: false,
53 emptyText: Proxmox.Utils.NoneText,
54 cbind: {
55 disabled: get => get('type') !== 'mds',
56 hidden: get => get('type') !== 'mds',
57 },
58 listeners: {
59 change: function(f, value) {
60 let view = this.up('pveCephCreateService');
61 view.setExtraID(value);
62 },
63 },
64 },
65 {
66 xtype: 'component',
67 border: false,
68 padding: '5 2',
69 style: {
70 fontSize: '12px',
71 },
72 userCls: 'pmx-hint',
73 cbind: {
74 hidden: get => get('type') !== 'mds',
75 },
76 html: gettext('The Extra ID allows creating multiple MDS per node, which increases redundancy with more than one CephFS.'),
77 },
a6c60aed
DC
78 ],
79
8058410f 80 initComponent: function() {
fb792310 81 let me = this;
a6c60aed
DC
82
83 if (!me.nodename) {
84 throw "no node name specified";
85 }
a6c60aed
DC
86 if (!me.type) {
87 throw "no type specified";
88 }
a6c60aed
DC
89 me.setNode(me.nodename);
90
91 me.callParent();
f6710aac 92 },
a6c60aed
DC
93});
94
89c04721
DC
95Ext.define('PVE.node.CephServiceController', {
96 extend: 'Ext.app.ViewController',
97 alias: 'controller.CephServiceList',
a6c60aed 98
999df1c2 99 render_status: (value, metadata, rec) => value,
a6c60aed 100
89c04721
DC
101 render_version: function(value, metadata, rec) {
102 if (value === undefined) {
103 return '';
104 }
105 let view = this.getView();
106 let host = rec.data.host, nodev = [0];
107 if (view.nodeversions[host] !== undefined) {
108 nodev = view.nodeversions[host].version.parts;
109 }
a6c60aed 110
89c04721
DC
111 let icon = '';
112 if (PVE.Utils.compare_ceph_versions(view.maxversion, nodev) > 0) {
113 icon = PVE.Utils.get_ceph_icon_html('HEALTH_UPGRADE');
114 } else if (PVE.Utils.compare_ceph_versions(nodev, value) > 0) {
115 icon = PVE.Utils.get_ceph_icon_html('HEALTH_OLD');
116 } else if (view.mixedversions) {
117 icon = PVE.Utils.get_ceph_icon_html('HEALTH_OK');
118 }
119 return icon + value;
120 },
a6c60aed 121
89c04721
DC
122 getMaxVersions: function(store, records, success) {
123 if (!success || records.length < 1) {
124 return;
125 }
126 let me = this;
127 let view = me.getView();
128
129 view.nodeversions = records[0].data.node;
130 view.maxversion = [];
131 view.mixedversions = false;
132 for (const [_nodename, data] of Object.entries(view.nodeversions)) {
133 let res = PVE.Utils.compare_ceph_versions(data.version.parts, view.maxversion);
134 if (res !== 0 && view.maxversion.length > 0) {
135 view.mixedversions = true;
7d88e195 136 }
89c04721
DC
137 if (res > 0) {
138 view.maxversion = data.version.parts;
277d6d15 139 }
89c04721
DC
140 }
141 },
277d6d15 142
89c04721
DC
143 init: function(view) {
144 if (view.pveSelNode) {
145 view.nodename = view.pveSelNode.data.node;
146 }
147 if (!view.nodename) {
148 throw "no node name specified";
149 }
277d6d15 150
89c04721
DC
151 if (!view.type) {
152 throw "no type specified";
153 }
277d6d15 154
89c04721
DC
155 view.versionsstore = Ext.create('Proxmox.data.UpdateStore', {
156 autoStart: true,
157 interval: 10000,
158 storeid: `ceph-versions-${view.type}-list${view.nodename}`,
159 proxy: {
160 type: 'proxmox',
161 url: "/api2/json/cluster/ceph/metadata?scope=versions",
162 },
163 });
164 view.versionsstore.on('load', this.getMaxVersions, this);
165 view.on('destroy', view.versionsstore.stopUpdate);
166
167 view.rstore = Ext.create('Proxmox.data.UpdateStore', {
168 autoStart: true,
169 interval: 3000,
170 storeid: `ceph-${view.type}-list${view.nodename}`,
171 model: 'ceph-service-list',
172 proxy: {
173 type: 'proxmox',
174 url: `/api2/json/nodes/${view.nodename}/ceph/${view.type}`,
175 },
176 });
a6c60aed 177
89c04721
DC
178 view.setStore(Ext.create('Proxmox.data.DiffStore', {
179 rstore: view.rstore,
180 sorters: [{ property: 'name' }],
181 }));
a6c60aed 182
89c04721
DC
183 if (view.storeLoadCallback) {
184 view.rstore.on('load', view.storeLoadCallback, this);
185 }
186 view.on('destroy', view.rstore.stopUpdate);
a6c60aed 187
89c04721
DC
188 if (view.showCephInstallMask) {
189 PVE.Utils.monitor_ceph_installed(view, view.rstore, view.nodename, true);
190 }
191 },
a6c60aed 192
89c04721
DC
193 service_cmd: function(rec, cmd) {
194 let view = this.getView();
195 if (!rec.data.host) {
196 Ext.Msg.alert(gettext('Error'), "entry has no host");
197 return;
198 }
6242a600
AL
199 let doRequest = function() {
200 Proxmox.Utils.API2Request({
201 url: `/nodes/${rec.data.host}/ceph/${cmd}`,
202 method: 'POST',
203 params: { service: view.type + '.' + rec.data.name },
204 success: function(response, options) {
205 Ext.create('Proxmox.window.TaskProgress', {
206 autoShow: true,
207 upid: response.result.data,
208 taskDone: () => view.rstore.load(),
209 });
210 },
211 failure: (response, _opts) => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
212 });
213 };
214 if (cmd === "stop" && ['mon', 'mds'].includes(view.type)) {
215 Proxmox.Utils.API2Request({
216 url: `/nodes/${rec.data.host}/ceph/cmd-safety`,
217 params: {
218 service: view.type,
219 id: rec.data.name,
220 action: 'stop',
221 },
222 method: 'GET',
223 success: function({ result: { data } }) {
224 let stopText = {
225 mon: gettext('Stop MON'),
226 mds: gettext('Stop MDS'),
227 };
228 if (!data.safe) {
229 Ext.Msg.show({
230 title: gettext('Warning'),
231 message: data.status,
232 icon: Ext.Msg.WARNING,
233 buttons: Ext.Msg.OKCANCEL,
234 buttonText: { ok: stopText[view.type] },
235 fn: function(selection) {
236 if (selection === 'ok') {
237 doRequest();
238 }
239 },
240 });
241 } else {
242 doRequest();
243 }
244 },
245 failure: (response, _opts) => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
246 });
247 } else {
248 doRequest();
249 }
89c04721
DC
250 },
251 onChangeService: function(button) {
252 let me = this;
253 let record = me.getView().getSelection()[0];
254 me.service_cmd(record, button.action);
255 },
a6c60aed 256
89c04721
DC
257 showSyslog: function() {
258 let view = this.getView();
259 let rec = view.getSelection()[0];
260 let service = `ceph-${view.type}@${rec.data.name}`;
261 Ext.create('Ext.window.Window', {
262 title: `${gettext('Syslog')}: ${service}`,
263 autoShow: true,
264 modal: true,
265 width: 800,
266 height: 400,
267 layout: 'fit',
268 items: [{
269 xtype: 'proxmoxLogView',
270 url: `/api2/extjs/nodes/${rec.data.host}/syslog?service=${encodeURIComponent(service)}`,
271 log_select_timespan: 1,
272 }],
273 });
274 },
a6c60aed 275
89c04721
DC
276 onCreate: function() {
277 let view = this.getView();
278 Ext.create('PVE.CephCreateService', {
279 autoShow: true,
280 nodename: view.nodename,
281 subject: view.getTitle(),
282 type: view.type,
283 taskDone: () => view.rstore.load(),
284 });
285 },
286});
a6c60aed 287
89c04721
DC
288Ext.define('PVE.node.CephServiceList', {
289 extend: 'Ext.grid.GridPanel',
290 xtype: 'pveNodeCephServiceList',
a6c60aed 291
89c04721
DC
292 onlineHelp: 'chapter_pveceph',
293 emptyText: gettext('No such service configured.'),
294
295 stateful: true,
296
297 // will be called when the store loads
298 storeLoadCallback: Ext.emptyFn,
299
300 // if set to true, does shows the ceph install mask if needed
301 showCephInstallMask: false,
302
303 controller: 'CephServiceList',
a6c60aed
DC
304
305 tbar: [
306 {
307 xtype: 'proxmoxButton',
308 text: gettext('Start'),
309 iconCls: 'fa fa-play',
310 action: 'start',
311 disabled: true,
fb792310 312 enableFn: rec => rec.data.state === 'stopped' || rec.data.state === 'unknown',
f6710aac 313 handler: 'onChangeService',
a6c60aed
DC
314 },
315 {
316 xtype: 'proxmoxButton',
317 text: gettext('Stop'),
318 iconCls: 'fa fa-stop',
319 action: 'stop',
fb792310 320 enableFn: rec => rec.data.state !== 'stopped',
a6c60aed 321 disabled: true,
f6710aac 322 handler: 'onChangeService',
a6c60aed
DC
323 },
324 {
325 xtype: 'proxmoxButton',
326 text: gettext('Restart'),
327 iconCls: 'fa fa-refresh',
328 action: 'restart',
329 disabled: true,
fb792310 330 enableFn: rec => rec.data.state !== 'stopped',
f6710aac 331 handler: 'onChangeService',
a6c60aed
DC
332 },
333 '-',
334 {
335 text: gettext('Create'),
336 reference: 'createButton',
f6710aac 337 handler: 'onCreate',
a6c60aed
DC
338 },
339 {
340 text: gettext('Destroy'),
341 xtype: 'proxmoxStdRemoveButton',
342 getUrl: function(rec) {
fb792310 343 let view = this.up('grid');
a6c60aed 344 if (!rec.data.host) {
fb792310
TL
345 Ext.Msg.alert(gettext('Error'), "entry has no host, cannot build API url");
346 return '';
a6c60aed 347 }
fb792310 348 return `/nodes/${rec.data.host}/ceph/${view.type}/${rec.data.name}`;
a6c60aed
DC
349 },
350 callback: function(options, success, response) {
fb792310 351 let view = this.up('grid');
a6c60aed
DC
352 if (!success) {
353 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
354 return;
355 }
fb792310
TL
356 Ext.create('Proxmox.window.TaskProgress', {
357 autoShow: true,
358 upid: response.result.data,
359 taskDone: () => view.rstore.load(),
a6c60aed 360 });
f6710aac 361 },
6242a600
AL
362 handler: function(btn, event, rec) {
363 let me = this;
364 let view = me.up('grid');
365 let doRequest = function() {
366 Proxmox.button.StdRemoveButton.prototype.handler.call(me, btn, event, rec);
367 };
368 if (view.type === 'mon') {
369 Proxmox.Utils.API2Request({
370 url: `/nodes/${rec.data.host}/ceph/cmd-safety`,
371 params: {
372 service: view.type,
373 id: rec.data.name,
374 action: 'destroy',
375 },
376 method: 'GET',
377 success: function({ result: { data } }) {
378 if (!data.safe) {
379 Ext.Msg.show({
380 title: gettext('Warning'),
381 message: data.status,
382 icon: Ext.Msg.WARNING,
383 buttons: Ext.Msg.OKCANCEL,
384 buttonText: { ok: gettext('Destroy MON') },
385 fn: function(selection) {
386 if (selection === 'ok') {
387 doRequest();
388 }
389 },
390 });
391 } else {
392 doRequest();
393 }
394 },
395 failure: (response, _opts) => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
396 });
397 } else {
398 doRequest();
399 }
400 },
401
a6c60aed
DC
402 },
403 '-',
404 {
405 xtype: 'proxmoxButton',
406 text: gettext('Syslog'),
407 disabled: true,
f6710aac
TL
408 handler: 'showSyslog',
409 },
a6c60aed
DC
410 ],
411
412 columns: [
413 {
414 header: gettext('Name'),
ee099a5f 415 flex: 1,
a6c60aed
DC
416 sortable: true,
417 renderer: function(v) {
418 return this.type + '.' + v;
419 },
f6710aac 420 dataIndex: 'name',
a6c60aed
DC
421 },
422 {
423 header: gettext('Host'),
ee099a5f 424 flex: 1,
a6c60aed
DC
425 sortable: true,
426 renderer: function(v) {
427 return v || Proxmox.Utils.unknownText;
428 },
f6710aac 429 dataIndex: 'host',
a6c60aed
DC
430 },
431 {
432 header: gettext('Status'),
ee099a5f 433 flex: 1,
a6c60aed 434 sortable: false,
999df1c2 435 renderer: 'render_status',
f6710aac 436 dataIndex: 'state',
a6c60aed
DC
437 },
438 {
439 header: gettext('Address'),
ee099a5f 440 flex: 3,
a6c60aed
DC
441 sortable: true,
442 renderer: function(v) {
443 return v || Proxmox.Utils.unknownText;
444 },
f6710aac 445 dataIndex: 'addr',
a6c60aed
DC
446 },
447 {
448 header: gettext('Version'),
ee099a5f 449 flex: 3,
a6c60aed 450 sortable: true,
277d6d15
DC
451 dataIndex: 'version',
452 renderer: 'render_version',
f6710aac 453 },
a6c60aed
DC
454 ],
455
456 initComponent: function() {
fb792310 457 let me = this;
a6c60aed
DC
458
459 if (me.additionalColumns) {
460 me.columns = me.columns.concat(me.additionalColumns);
461 }
462
463 me.callParent();
f6710aac 464 },
a6c60aed
DC
465
466}, function() {
a6c60aed
DC
467 Ext.define('ceph-service-list', {
468 extend: 'Ext.data.Model',
fb792310
TL
469 fields: [
470 'addr',
471 'name',
999df1c2 472 'fs_name',
fb792310
TL
473 'rank',
474 'host',
475 'quorum',
476 'state',
477 'ceph_version',
478 'ceph_version_short',
8058410f 479 {
fb792310
TL
480 type: 'string',
481 name: 'version',
482 calculate: data => PVE.Utils.parse_ceph_version(data),
fa8d3971 483 },
a6c60aed 484 ],
f6710aac 485 idProperty: 'name',
a6c60aed
DC
486 });
487});
999df1c2 488
8ce85523
TL
489Ext.define('PVE.node.CephMDSServiceController', {
490 extend: 'PVE.node.CephServiceController',
491 alias: 'controller.CephServiceMDSList',
492
493 render_status: (value, mD, rec) => rec.data.fs_name ? `${value} (${rec.data.fs_name})` : value,
494});
495
999df1c2
DC
496Ext.define('PVE.node.CephMDSList', {
497 extend: 'PVE.node.CephServiceList',
498 xtype: 'pveNodeCephMDSList',
499
500 controller: {
8ce85523 501 type: 'CephServiceMDSList',
999df1c2
DC
502 },
503});
504