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