]>
Commit | Line | Data |
---|---|---|
2f13e1d2 TL |
1 | Ext.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 | ||
14 | Ext.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 | ||
22 | Ext.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 | }); |