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