]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/dc/Cluster.js
ui: cluster info: fix displaying error
[pve-manager.git] / www / manager6 / dc / Cluster.js
CommitLineData
2f13e1d2
TL
1Ext.define('pve-cluster-nodes', {
2 extend: 'Ext.data.Model',
3 fields: [
4 'node', { type: 'integer', name: 'nodeid' }, 'ring0_addr', 'ring1_addr',
f6710aac 5 { type: 'integer', name: 'quorum_votes' },
2f13e1d2
TL
6 ],
7 proxy: {
8 type: 'proxmox',
f6710aac 9 url: "/api2/json/cluster/config/nodes",
2f13e1d2 10 },
f6710aac 11 idProperty: 'nodeid',
2f13e1d2
TL
12});
13
14Ext.define('pve-cluster-info', {
15 extend: 'Ext.data.Model',
16 proxy: {
17 type: 'proxmox',
f6710aac
TL
18 url: "/api2/json/cluster/config/join",
19 },
2f13e1d2
TL
20});
21
22Ext.define('PVE.ClusterAdministration', {
23 extend: 'Ext.panel.Panel',
24 xtype: 'pveClusterAdministration',
25
26 title: gettext('Cluster Administration'),
16dedd0f 27 onlineHelp: 'chapter_pvecm',
2f13e1d2
TL
28
29 border: false,
30 defaults: { border: false },
31
32 viewModel: {
33 parent: null,
34 data: {
35 totem: {},
36 nodelist: [],
37 preferred_node: {
38 name: '',
39 fp: '',
f6710aac 40 addr: '',
2f13e1d2
TL
41 },
42 isInCluster: false,
f6710aac
TL
43 nodecount: 0,
44 },
2f13e1d2
TL
45 },
46
47 items: [
48 {
49 xtype: 'panel',
50 title: gettext('Cluster Information'),
51 controller: {
52 xclass: 'Ext.app.ViewController',
53
54 init: function(view) {
55 view.store = Ext.create('Proxmox.data.UpdateStore', {
56 autoStart: true,
57 interval: 15 * 1000,
58 storeid: 'pve-cluster-info',
f6710aac 59 model: 'pve-cluster-info',
2f13e1d2
TL
60 });
61 view.store.on('load', this.onLoad, this);
62 view.on('destroy', view.store.stopUpdate);
63 },
64
9ab0c069 65 onLoad: function(store, records, success, operation) {
4ed38e25 66 let vm = this.getViewModel();
6152988e
TL
67
68 let data = records?.[0]?.data;
69 if (!success || !data || !data.nodelist?.length) {
9ab0c069 70 let error = operation.getError();
1b72acbf
TL
71 if (error) {
72 let msg = Proxmox.Utils.getResponseErrorMessage(error);
73 if (error.status !== 424 && !msg.match(/node is not in a cluster/i)) {
74 // an actual error, not just the "not in a cluster one", so show it!
b7b7b661 75 Proxmox.Utils.setErrorMask(this.getView(), msg);
1b72acbf 76 }
9ab0c069 77 }
2f13e1d2
TL
78 vm.set('totem', {});
79 vm.set('isInCluster', false);
80 vm.set('nodelist', []);
81 vm.set('preferred_node', {
82 name: '',
83 addr: '',
f6710aac 84 fp: '',
2f13e1d2
TL
85 });
86 return;
87 }
2f13e1d2
TL
88 vm.set('totem', data.totem);
89 vm.set('isInCluster', !!data.totem.cluster_name);
90 vm.set('nodelist', data.nodelist);
91
6152988e 92 let nodeinfo = data.nodelist.find(el => el.name === data.preferred_node);
2f13e1d2 93
b1e7a7d3
SR
94 let links = {};
95 let ring_addr = [];
96 PVE.Utils.forEachCorosyncLink(nodeinfo, (num, link) => {
97 links[num] = link;
98 ring_addr.push(link);
99 });
e65817a1 100
2f13e1d2
TL
101 vm.set('preferred_node', {
102 name: data.preferred_node,
103 addr: nodeinfo.pve_addr,
b1e7a7d3
SR
104 peerLinks: links,
105 ring_addr: ring_addr,
f6710aac 106 fp: nodeinfo.pve_fp,
2f13e1d2
TL
107 });
108 },
8ad2b3a1
TL
109
110 onCreate: function() {
4ed38e25 111 let view = this.getView();
8ad2b3a1 112 view.store.stopUpdate();
4ed38e25 113 Ext.create('PVE.ClusterCreateWindow', {
8ad2b3a1
TL
114 autoShow: true,
115 listeners: {
116 destroy: function() {
117 view.store.startUpdate();
f6710aac
TL
118 },
119 },
8ad2b3a1 120 });
9cc0013b
TL
121 },
122
123 onClusterInfo: function() {
4ed38e25
TL
124 let vm = this.getViewModel();
125 Ext.create('PVE.ClusterInfoWindow', {
126 autoShow: true,
9cc0013b
TL
127 joinInfo: {
128 ipAddress: vm.get('preferred_node.addr'),
129 fingerprint: vm.get('preferred_node.fp'),
b1e7a7d3 130 peerLinks: vm.get('preferred_node.peerLinks'),
9e3b13eb 131 ring_addr: vm.get('preferred_node.ring_addr'),
f6710aac
TL
132 totem: vm.get('totem'),
133 },
9cc0013b 134 });
f67e1cc0
TL
135 },
136
137 onJoin: function() {
4ed38e25 138 let view = this.getView();
f67e1cc0 139 view.store.stopUpdate();
4ed38e25 140 Ext.create('PVE.ClusterJoinNodeWindow', {
f67e1cc0
TL
141 autoShow: true,
142 listeners: {
143 destroy: function() {
144 view.store.startUpdate();
f6710aac
TL
145 },
146 },
f67e1cc0 147 });
f6710aac 148 },
2f13e1d2 149 },
8ad2b3a1
TL
150 tbar: [
151 {
152 text: gettext('Create Cluster'),
153 reference: 'createButton',
154 handler: 'onCreate',
155 bind: {
f6710aac
TL
156 disabled: '{isInCluster}',
157 },
9cc0013b
TL
158 },
159 {
160 text: gettext('Join Information'),
161 reference: 'addButton',
162 handler: 'onClusterInfo',
163 bind: {
f6710aac
TL
164 disabled: '{!isInCluster}',
165 },
f67e1cc0
TL
166 },
167 {
168 text: gettext('Join Cluster'),
169 reference: 'joinButton',
170 handler: 'onJoin',
171 bind: {
f6710aac
TL
172 disabled: '{isInCluster}',
173 },
174 },
8ad2b3a1 175 ],
2f13e1d2
TL
176 layout: 'hbox',
177 bodyPadding: 5,
178 items: [
179 {
180 xtype: 'displayfield',
181 fieldLabel: gettext('Cluster Name'),
182 bind: {
183 value: '{totem.cluster_name}',
f6710aac 184 hidden: '{!isInCluster}',
2f13e1d2 185 },
f6710aac 186 flex: 1,
2f13e1d2
TL
187 },
188 {
189 xtype: 'displayfield',
190 fieldLabel: gettext('Config Version'),
191 bind: {
192 value: '{totem.config_version}',
f6710aac 193 hidden: '{!isInCluster}',
2f13e1d2 194 },
f6710aac 195 flex: 1,
2f13e1d2
TL
196 },
197 {
198 xtype: 'displayfield',
199 fieldLabel: gettext('Number of Nodes'),
200 labelWidth: 120,
201 bind: {
202 value: '{nodecount}',
f6710aac 203 hidden: '{!isInCluster}',
2f13e1d2 204 },
f6710aac 205 flex: 1,
2f13e1d2
TL
206 },
207 {
208 xtype: 'displayfield',
209 value: gettext('Standalone node - no cluster defined'),
210 bind: {
f6710aac 211 hidden: '{isInCluster}',
2f13e1d2 212 },
f6710aac
TL
213 flex: 1,
214 },
215 ],
2f13e1d2
TL
216 },
217 {
218 xtype: 'grid',
219 title: gettext('Cluster Nodes'),
e65817a1
SR
220 autoScroll: true,
221 enableColumnHide: false,
2f13e1d2
TL
222 controller: {
223 xclass: 'Ext.app.ViewController',
224
225 init: function(view) {
226 view.rstore = Ext.create('Proxmox.data.UpdateStore', {
227 autoLoad: true,
228 xtype: 'update',
229 interval: 5 * 1000,
230 autoStart: true,
231 storeid: 'pve-cluster-nodes',
f6710aac 232 model: 'pve-cluster-nodes',
2f13e1d2
TL
233 });
234 view.setStore(Ext.create('Proxmox.data.DiffStore', {
235 rstore: view.rstore,
236 sorters: {
237 property: 'nodeid',
392e3cf1 238 direction: 'ASC',
f6710aac 239 },
2f13e1d2
TL
240 }));
241 Proxmox.Utils.monStoreErrors(view, view.rstore);
f95d79e3 242 view.rstore.on('load', this.onLoad, this);
2f13e1d2
TL
243 view.on('destroy', view.rstore.stopUpdate);
244 },
245
246 onLoad: function(store, records, success) {
4ed38e25
TL
247 let view = this.getView();
248 let vm = this.getViewModel();
e65817a1
SR
249
250 if (!success || !records || !records.length) {
2f13e1d2
TL
251 vm.set('nodecount', 0);
252 return;
253 }
254 vm.set('nodecount', records.length);
e65817a1
SR
255
256 // show/hide columns according to used links
4ed38e25
TL
257 let linkIndex = view.columns.length;
258 Ext.each(view.columns, (col, i) => {
e65817a1
SR
259 if (col.linkNumber !== undefined) {
260 col.setHidden(true);
4ed38e25 261 // save offset at which link columns start, so we can address them directly below
e65817a1
SR
262 if (i < linkIndex) {
263 linkIndex = i;
264 }
265 }
266 });
267
268 PVE.Utils.forEachCorosyncLink(records[0].data,
269 (linknum, val) => {
270 if (linknum > 7) {
271 return;
272 }
4ed38e25 273 view.columns[linkIndex + linknum].setHidden(false);
f6710aac 274 },
e65817a1 275 );
f6710aac 276 },
2f13e1d2 277 },
e65817a1
SR
278 columns: {
279 items: [
280 {
281 header: gettext('Nodename'),
282 hidden: false,
f6710aac 283 dataIndex: 'name',
e65817a1
SR
284 },
285 {
286 header: gettext('ID'),
287 minWidth: 100,
288 width: 100,
289 flex: 0,
290 hidden: false,
f6710aac 291 dataIndex: 'nodeid',
e65817a1
SR
292 },
293 {
294 header: gettext('Votes'),
295 minWidth: 100,
296 width: 100,
297 flex: 0,
298 hidden: false,
f6710aac 299 dataIndex: 'quorum_votes',
e65817a1
SR
300 },
301 {
302 header: Ext.String.format(gettext('Link {0}'), 0),
303 dataIndex: 'ring0_addr',
f6710aac 304 linkNumber: 0,
e65817a1
SR
305 },
306 {
307 header: Ext.String.format(gettext('Link {0}'), 1),
308 dataIndex: 'ring1_addr',
f6710aac 309 linkNumber: 1,
e65817a1
SR
310 },
311 {
312 header: Ext.String.format(gettext('Link {0}'), 2),
313 dataIndex: 'ring2_addr',
f6710aac 314 linkNumber: 2,
e65817a1
SR
315 },
316 {
317 header: Ext.String.format(gettext('Link {0}'), 3),
318 dataIndex: 'ring3_addr',
f6710aac 319 linkNumber: 3,
e65817a1
SR
320 },
321 {
322 header: Ext.String.format(gettext('Link {0}'), 4),
323 dataIndex: 'ring4_addr',
f6710aac 324 linkNumber: 4,
e65817a1
SR
325 },
326 {
327 header: Ext.String.format(gettext('Link {0}'), 5),
328 dataIndex: 'ring5_addr',
f6710aac 329 linkNumber: 5,
e65817a1
SR
330 },
331 {
332 header: Ext.String.format(gettext('Link {0}'), 6),
333 dataIndex: 'ring6_addr',
f6710aac 334 linkNumber: 6,
e65817a1
SR
335 },
336 {
337 header: Ext.String.format(gettext('Link {0}'), 7),
338 dataIndex: 'ring7_addr',
f6710aac
TL
339 linkNumber: 7,
340 },
e65817a1
SR
341 ],
342 defaults: {
2f13e1d2 343 flex: 1,
e65817a1 344 hidden: true,
f6710aac
TL
345 minWidth: 150,
346 },
347 },
348 },
349 ],
2f13e1d2 350});