]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/ceph/ServiceList.js
9298974e8c46f1cbe21987b488892ad85394a9bd
[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 Proxmox.Utils.API2Request({
152 url: `/nodes/${rec.data.host}/ceph/${cmd}`,
153 method: 'POST',
154 params: { service: view.type + '.' + rec.data.name },
155 success: function(response, options) {
156 Ext.create('Proxmox.window.TaskProgress', {
157 autoShow: true,
158 upid: response.result.data,
159 taskDone: () => view.rstore.load(),
160 });
161 },
162 failure: (response, _opts) => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
163 });
164 },
165 onChangeService: function(button) {
166 let me = this;
167 let record = me.getView().getSelection()[0];
168 me.service_cmd(record, button.action);
169 },
170
171 showSyslog: function() {
172 let view = this.getView();
173 let rec = view.getSelection()[0];
174 let service = `ceph-${view.type}@${rec.data.name}`;
175 Ext.create('Ext.window.Window', {
176 title: `${gettext('Syslog')}: ${service}`,
177 autoShow: true,
178 modal: true,
179 width: 800,
180 height: 400,
181 layout: 'fit',
182 items: [{
183 xtype: 'proxmoxLogView',
184 url: `/api2/extjs/nodes/${rec.data.host}/syslog?service=${encodeURIComponent(service)}`,
185 log_select_timespan: 1,
186 }],
187 });
188 },
189
190 onCreate: function() {
191 let view = this.getView();
192 Ext.create('PVE.CephCreateService', {
193 autoShow: true,
194 nodename: view.nodename,
195 subject: view.getTitle(),
196 type: view.type,
197 taskDone: () => view.rstore.load(),
198 });
199 },
200 });
201
202 Ext.define('PVE.node.CephServiceList', {
203 extend: 'Ext.grid.GridPanel',
204 xtype: 'pveNodeCephServiceList',
205
206 onlineHelp: 'chapter_pveceph',
207 emptyText: gettext('No such service configured.'),
208
209 stateful: true,
210
211 // will be called when the store loads
212 storeLoadCallback: Ext.emptyFn,
213
214 // if set to true, does shows the ceph install mask if needed
215 showCephInstallMask: false,
216
217 controller: 'CephServiceList',
218
219 tbar: [
220 {
221 xtype: 'proxmoxButton',
222 text: gettext('Start'),
223 iconCls: 'fa fa-play',
224 action: 'start',
225 disabled: true,
226 enableFn: rec => rec.data.state === 'stopped' || rec.data.state === 'unknown',
227 handler: 'onChangeService',
228 },
229 {
230 xtype: 'proxmoxButton',
231 text: gettext('Stop'),
232 iconCls: 'fa fa-stop',
233 action: 'stop',
234 enableFn: rec => rec.data.state !== 'stopped',
235 disabled: true,
236 handler: 'onChangeService',
237 },
238 {
239 xtype: 'proxmoxButton',
240 text: gettext('Restart'),
241 iconCls: 'fa fa-refresh',
242 action: 'restart',
243 disabled: true,
244 enableFn: rec => rec.data.state !== 'stopped',
245 handler: 'onChangeService',
246 },
247 '-',
248 {
249 text: gettext('Create'),
250 reference: 'createButton',
251 handler: 'onCreate',
252 },
253 {
254 text: gettext('Destroy'),
255 xtype: 'proxmoxStdRemoveButton',
256 getUrl: function(rec) {
257 let view = this.up('grid');
258 if (!rec.data.host) {
259 Ext.Msg.alert(gettext('Error'), "entry has no host, cannot build API url");
260 return '';
261 }
262 return `/nodes/${rec.data.host}/ceph/${view.type}/${rec.data.name}`;
263 },
264 callback: function(options, success, response) {
265 let view = this.up('grid');
266 if (!success) {
267 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
268 return;
269 }
270 Ext.create('Proxmox.window.TaskProgress', {
271 autoShow: true,
272 upid: response.result.data,
273 taskDone: () => view.rstore.load(),
274 });
275 },
276 },
277 '-',
278 {
279 xtype: 'proxmoxButton',
280 text: gettext('Syslog'),
281 disabled: true,
282 handler: 'showSyslog',
283 },
284 ],
285
286 columns: [
287 {
288 header: gettext('Name'),
289 flex: 1,
290 sortable: true,
291 renderer: function(v) {
292 return this.type + '.' + v;
293 },
294 dataIndex: 'name',
295 },
296 {
297 header: gettext('Host'),
298 flex: 1,
299 sortable: true,
300 renderer: function(v) {
301 return v || Proxmox.Utils.unknownText;
302 },
303 dataIndex: 'host',
304 },
305 {
306 header: gettext('Status'),
307 flex: 1,
308 sortable: false,
309 renderer: 'render_status',
310 dataIndex: 'state',
311 },
312 {
313 header: gettext('Address'),
314 flex: 3,
315 sortable: true,
316 renderer: function(v) {
317 return v || Proxmox.Utils.unknownText;
318 },
319 dataIndex: 'addr',
320 },
321 {
322 header: gettext('Version'),
323 flex: 3,
324 sortable: true,
325 dataIndex: 'version',
326 renderer: 'render_version',
327 },
328 ],
329
330 initComponent: function() {
331 let me = this;
332
333 if (me.additionalColumns) {
334 me.columns = me.columns.concat(me.additionalColumns);
335 }
336
337 me.callParent();
338 },
339
340 }, function() {
341 Ext.define('ceph-service-list', {
342 extend: 'Ext.data.Model',
343 fields: [
344 'addr',
345 'name',
346 'fs_name',
347 'rank',
348 'host',
349 'quorum',
350 'state',
351 'ceph_version',
352 'ceph_version_short',
353 {
354 type: 'string',
355 name: 'version',
356 calculate: data => PVE.Utils.parse_ceph_version(data),
357 },
358 ],
359 idProperty: 'name',
360 });
361 });
362
363 Ext.define('PVE.node.CephMDSServiceController', {
364 extend: 'PVE.node.CephServiceController',
365 alias: 'controller.CephServiceMDSList',
366
367 render_status: (value, mD, rec) => rec.data.fs_name ? `${value} (${rec.data.fs_name})` : value,
368 });
369
370 Ext.define('PVE.node.CephMDSList', {
371 extend: 'PVE.node.CephServiceList',
372 xtype: 'pveNodeCephMDSList',
373
374 controller: {
375 type: 'CephServiceMDSList',
376 },
377 });
378