]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/ceph/ServiceList.js
ui: ceph: catch missing version for service list
[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.CephServiceList', {
48 extend: 'Ext.grid.GridPanel',
49 xtype: 'pveNodeCephServiceList',
50
51 onlineHelp: 'chapter_pveceph',
52 emptyText: gettext('No such service configured.'),
53
54 stateful: true,
55
56 // will be called when the store loads
57 storeLoadCallback: Ext.emptyFn,
58
59 // if set to true, does shows the ceph install mask if needed
60 showCephInstallMask: false,
61
62 controller: {
63 xclass: 'Ext.app.ViewController',
64
65 render_version: function(value, metadata, rec) {
66 if (value === undefined) {
67 return '';
68 }
69 let view = this.getView();
70 let host = rec.data.host, nodev = [0];
71 if (view.nodeversions[host] !== undefined) {
72 nodev = view.nodeversions[host].version.parts;
73 }
74
75 let icon = '';
76 if (PVE.Utils.compare_ceph_versions(view.maxversion, nodev) > 0) {
77 icon = PVE.Utils.get_ceph_icon_html('HEALTH_UPGRADE');
78 } else if (PVE.Utils.compare_ceph_versions(nodev, value) > 0) {
79 icon = PVE.Utils.get_ceph_icon_html('HEALTH_OLD');
80 } else if (view.mixedversions) {
81 icon = PVE.Utils.get_ceph_icon_html('HEALTH_OK');
82 }
83 return icon + value;
84 },
85
86 getMaxVersions: function(store, records, success) {
87 if (!success || records.length < 1) {
88 return;
89 }
90 let me = this;
91 let view = me.getView();
92
93 view.nodeversions = records[0].data.node;
94 view.maxversion = [];
95 view.mixedversions = false;
96 for (const [_nodename, data] of Object.entries(view.nodeversions)) {
97 let res = PVE.Utils.compare_ceph_versions(data.version.parts, view.maxversion);
98 if (res !== 0 && view.maxversion.length > 0) {
99 view.mixedversions = true;
100 }
101 if (res > 0) {
102 view.maxversion = data.version.parts;
103 }
104 }
105 },
106
107 init: function(view) {
108 if (view.pveSelNode) {
109 view.nodename = view.pveSelNode.data.node;
110 }
111 if (!view.nodename) {
112 throw "no node name specified";
113 }
114
115 if (!view.type) {
116 throw "no type specified";
117 }
118
119 view.versionsstore = Ext.create('Proxmox.data.UpdateStore', {
120 autoStart: true,
121 interval: 10000,
122 storeid: `ceph-versions-${view.type}-list${view.nodename}`,
123 proxy: {
124 type: 'proxmox',
125 url: "/api2/json/cluster/ceph/metadata?scope=versions",
126 },
127 });
128 view.versionsstore.on('load', this.getMaxVersions, this);
129 view.on('destroy', view.versionsstore.stopUpdate);
130
131 view.rstore = Ext.create('Proxmox.data.UpdateStore', {
132 autoStart: true,
133 interval: 3000,
134 storeid: `ceph-${view.type}-list${view.nodename}`,
135 model: 'ceph-service-list',
136 proxy: {
137 type: 'proxmox',
138 url: `/api2/json/nodes/${view.nodename}/ceph/${view.type}`,
139 },
140 });
141
142 view.setStore(Ext.create('Proxmox.data.DiffStore', {
143 rstore: view.rstore,
144 sorters: [{ property: 'name' }],
145 }));
146
147 if (view.storeLoadCallback) {
148 view.rstore.on('load', view.storeLoadCallback, this);
149 }
150 view.on('destroy', view.rstore.stopUpdate);
151
152 if (view.showCephInstallMask) {
153 PVE.Utils.monitor_ceph_installed(view, view.rstore, view.nodename, true);
154 }
155 },
156
157 service_cmd: function(rec, cmd) {
158 let view = this.getView();
159 if (!rec.data.host) {
160 Ext.Msg.alert(gettext('Error'), "entry has no host");
161 return;
162 }
163 Proxmox.Utils.API2Request({
164 url: `/nodes/${rec.data.host}/ceph/${cmd}`,
165 method: 'POST',
166 params: { service: view.type + '.' + rec.data.name },
167 success: function(response, options) {
168 Ext.create('Proxmox.window.TaskProgress', {
169 autoShow: true,
170 upid: response.result.data,
171 taskDone: () => view.rstore.load(),
172 });
173 },
174 failure: (response, _opts) => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
175 });
176 },
177 onChangeService: function(button) {
178 let me = this;
179 let record = me.getView().getSelection()[0];
180 me.service_cmd(record, button.action);
181 },
182
183 showSyslog: function() {
184 let view = this.getView();
185 let rec = view.getSelection()[0];
186 let service = `ceph-${view.type}@${rec.data.name}`;
187 Ext.create('Ext.window.Window', {
188 title: `${gettext('Syslog')}: ${service}`,
189 autoShow: true,
190 modal: true,
191 width: 800,
192 height: 400,
193 layout: 'fit',
194 items: [{
195 xtype: 'proxmoxLogView',
196 url: `/api2/extjs/nodes/${rec.data.host}/syslog?service=${encodeURIComponent(service)}`,
197 log_select_timespan: 1,
198 }],
199 });
200 },
201
202 onCreate: function() {
203 let view = this.getView();
204 Ext.create('PVE.CephCreateService', {
205 autoShow: true,
206 nodename: view.nodename,
207 subject: view.getTitle(),
208 type: view.type,
209 taskDone: () => view.rstore.load(),
210 });
211 },
212 },
213
214 tbar: [
215 {
216 xtype: 'proxmoxButton',
217 text: gettext('Start'),
218 iconCls: 'fa fa-play',
219 action: 'start',
220 disabled: true,
221 enableFn: rec => rec.data.state === 'stopped' || rec.data.state === 'unknown',
222 handler: 'onChangeService',
223 },
224 {
225 xtype: 'proxmoxButton',
226 text: gettext('Stop'),
227 iconCls: 'fa fa-stop',
228 action: 'stop',
229 enableFn: rec => rec.data.state !== 'stopped',
230 disabled: true,
231 handler: 'onChangeService',
232 },
233 {
234 xtype: 'proxmoxButton',
235 text: gettext('Restart'),
236 iconCls: 'fa fa-refresh',
237 action: 'restart',
238 disabled: true,
239 enableFn: rec => rec.data.state !== 'stopped',
240 handler: 'onChangeService',
241 },
242 '-',
243 {
244 text: gettext('Create'),
245 reference: 'createButton',
246 handler: 'onCreate',
247 },
248 {
249 text: gettext('Destroy'),
250 xtype: 'proxmoxStdRemoveButton',
251 getUrl: function(rec) {
252 let view = this.up('grid');
253 if (!rec.data.host) {
254 Ext.Msg.alert(gettext('Error'), "entry has no host, cannot build API url");
255 return '';
256 }
257 return `/nodes/${rec.data.host}/ceph/${view.type}/${rec.data.name}`;
258 },
259 callback: function(options, success, response) {
260 let view = this.up('grid');
261 if (!success) {
262 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
263 return;
264 }
265 Ext.create('Proxmox.window.TaskProgress', {
266 autoShow: true,
267 upid: response.result.data,
268 taskDone: () => view.rstore.load(),
269 });
270 },
271 },
272 '-',
273 {
274 xtype: 'proxmoxButton',
275 text: gettext('Syslog'),
276 disabled: true,
277 handler: 'showSyslog',
278 },
279 ],
280
281 columns: [
282 {
283 header: gettext('Name'),
284 flex: 1,
285 sortable: true,
286 renderer: function(v) {
287 return this.type + '.' + v;
288 },
289 dataIndex: 'name',
290 },
291 {
292 header: gettext('Host'),
293 flex: 1,
294 sortable: true,
295 renderer: function(v) {
296 return v || Proxmox.Utils.unknownText;
297 },
298 dataIndex: 'host',
299 },
300 {
301 header: gettext('Status'),
302 flex: 1,
303 sortable: false,
304 dataIndex: 'state',
305 },
306 {
307 header: gettext('Address'),
308 flex: 3,
309 sortable: true,
310 renderer: function(v) {
311 return v || Proxmox.Utils.unknownText;
312 },
313 dataIndex: 'addr',
314 },
315 {
316 header: gettext('Version'),
317 flex: 3,
318 sortable: true,
319 dataIndex: 'version',
320 renderer: 'render_version',
321 },
322 ],
323
324 initComponent: function() {
325 let me = this;
326
327 if (me.additionalColumns) {
328 me.columns = me.columns.concat(me.additionalColumns);
329 }
330
331 me.callParent();
332 },
333
334 }, function() {
335 Ext.define('ceph-service-list', {
336 extend: 'Ext.data.Model',
337 fields: [
338 'addr',
339 'name',
340 'rank',
341 'host',
342 'quorum',
343 'state',
344 'ceph_version',
345 'ceph_version_short',
346 {
347 type: 'string',
348 name: 'version',
349 calculate: data => PVE.Utils.parse_ceph_version(data),
350 },
351 ],
352 idProperty: 'name',
353 });
354 });