]>
Commit | Line | Data |
---|---|---|
8ad2b3a1 TL |
1 | Ext.define('PVE.ClusterCreateWindow', { |
2 | extend: 'Proxmox.window.Edit', | |
3 | xtype: 'pveClusterCreateWindow', | |
4 | ||
5 | title: gettext('Create Cluster'), | |
6 | width: 600, | |
7 | ||
8 | method: 'POST', | |
9 | url: '/cluster/config', | |
10 | ||
11 | isCreate: true, | |
12 | subject: gettext('Cluster'), | |
13 | showTaskViewer: true, | |
14 | ||
c4594334 TL |
15 | onlineHelp: 'pvecm_create_cluster', |
16 | ||
e20eda89 TL |
17 | items: { |
18 | xtype: 'inputpanel', | |
19 | items: [{ | |
8ad2b3a1 TL |
20 | xtype: 'textfield', |
21 | fieldLabel: gettext('Cluster Name'), | |
22 | allowBlank: false, | |
63584726 | 23 | maxLength: 15, |
f6710aac | 24 | name: 'clustername', |
8ad2b3a1 TL |
25 | }, |
26 | { | |
5957f47f | 27 | xtype: 'fieldcontainer', |
41aa53ea | 28 | fieldLabel: gettext("Cluster Network"), |
5957f47f SR |
29 | items: [ |
30 | { | |
31 | xtype: 'pveCorosyncLinkEditor', | |
41aa53ea | 32 | infoText: gettext("Multiple links are used as failover, lower numbers have higher priority."), |
f6710aac | 33 | name: 'links', |
5957f47f | 34 | }, |
f6710aac TL |
35 | ], |
36 | }], | |
37 | }, | |
8ad2b3a1 | 38 | }); |
9cc0013b TL |
39 | |
40 | Ext.define('PVE.ClusterInfoWindow', { | |
41 | extend: 'Ext.window.Window', | |
42 | xtype: 'pveClusterInfoWindow', | |
43 | mixins: ['Proxmox.Mixin.CBind'], | |
44 | ||
45 | width: 800, | |
46 | modal: true, | |
47 | resizable: false, | |
48 | title: gettext('Cluster Join Information'), | |
49 | ||
50 | joinInfo: { | |
51 | ipAddress: undefined, | |
52 | fingerprint: undefined, | |
f6710aac | 53 | totem: {}, |
9cc0013b TL |
54 | }, |
55 | ||
56 | items: [ | |
57 | { | |
58 | xtype: 'component', | |
59 | border: false, | |
60 | padding: '10 10 10 10', | |
f6710aac | 61 | html: gettext("Copy the Join Information here and use it on the node you want to add."), |
9cc0013b TL |
62 | }, |
63 | { | |
64 | xtype: 'container', | |
65 | layout: 'form', | |
66 | border: false, | |
67 | padding: '0 10 10 10', | |
68 | items: [ | |
69 | { | |
70 | xtype: 'textfield', | |
71 | fieldLabel: gettext('IP Address'), | |
9a9f1ac5 TL |
72 | cbind: { |
73 | value: '{joinInfo.ipAddress}', | |
74 | }, | |
75 | editable: false, | |
9cc0013b TL |
76 | }, |
77 | { | |
78 | xtype: 'textfield', | |
79 | fieldLabel: gettext('Fingerprint'), | |
9a9f1ac5 TL |
80 | cbind: { |
81 | value: '{joinInfo.fingerprint}', | |
82 | }, | |
83 | editable: false, | |
9cc0013b TL |
84 | }, |
85 | { | |
86 | xtype: 'textarea', | |
87 | inputId: 'pveSerializedClusterInfo', | |
88 | fieldLabel: gettext('Join Information'), | |
89 | grow: true, | |
9a9f1ac5 TL |
90 | cbind: { |
91 | joinInfo: '{joinInfo}', | |
92 | }, | |
9cc0013b TL |
93 | editable: false, |
94 | listeners: { | |
95 | afterrender: function(field) { | |
96 | if (!field.joinInfo) { | |
97 | return; | |
98 | } | |
99 | var jsons = Ext.JSON.encode(field.joinInfo); | |
100 | var base64s = Ext.util.Base64.encode(jsons); | |
101 | field.setValue(base64s); | |
f6710aac TL |
102 | }, |
103 | }, | |
104 | }, | |
105 | ], | |
106 | }, | |
9cc0013b TL |
107 | ], |
108 | dockedItems: [{ | |
109 | dock: 'bottom', | |
110 | xtype: 'toolbar', | |
111 | items: [{ | |
112 | xtype: 'button', | |
113 | handler: function(b) { | |
114 | var el = document.getElementById('pveSerializedClusterInfo'); | |
115 | el.select(); | |
116 | document.execCommand("copy"); | |
117 | }, | |
f6710aac TL |
118 | text: gettext('Copy Information'), |
119 | }], | |
120 | }], | |
9cc0013b | 121 | }); |
f67e1cc0 TL |
122 | |
123 | Ext.define('PVE.ClusterJoinNodeWindow', { | |
124 | extend: 'Proxmox.window.Edit', | |
125 | xtype: 'pveClusterJoinNodeWindow', | |
126 | ||
127 | title: gettext('Cluster Join'), | |
128 | width: 800, | |
129 | ||
130 | method: 'POST', | |
131 | url: '/cluster/config/join', | |
132 | ||
133 | defaultFocus: 'textarea[name=serializedinfo]', | |
134 | isCreate: true, | |
a76a8a7d TL |
135 | bind: { |
136 | submitText: '{submittxt}', | |
137 | }, | |
f67e1cc0 TL |
138 | showTaskViewer: true, |
139 | ||
a202f65a | 140 | onlineHelp: 'pvecm_join_node_to_cluster', |
f67e1cc0 TL |
141 | |
142 | viewModel: { | |
143 | parent: null, | |
144 | data: { | |
145 | info: { | |
146 | fp: '', | |
147 | ip: '', | |
f6710aac | 148 | clusterName: '', |
7788262a TL |
149 | }, |
150 | hasAssistedInfo: false, | |
9e3b13eb TL |
151 | }, |
152 | formulas: { | |
a76a8a7d TL |
153 | submittxt: function(get) { |
154 | let cn = get('info.clusterName'); | |
155 | if (cn) { | |
943672c6 | 156 | return Ext.String.format(gettext('Join {0}'), `'${cn}'`); |
a76a8a7d TL |
157 | } |
158 | return gettext('Join'); | |
159 | }, | |
7788262a TL |
160 | showClusterFields: (get) => { |
161 | let manualMode = !get('assistedEntry.checked'); | |
162 | return get('hasAssistedInfo') || manualMode; | |
163 | }, | |
a76a8a7d | 164 | }, |
f67e1cc0 TL |
165 | }, |
166 | ||
167 | controller: { | |
168 | xclass: 'Ext.app.ViewController', | |
169 | control: { | |
fe13284e TL |
170 | '#': { |
171 | close: function() { | |
172 | delete PVE.Utils.silenceAuthFailures; | |
f6710aac | 173 | }, |
fe13284e | 174 | }, |
08e0aba8 | 175 | 'proxmoxcheckbox[name=assistedEntry]': { |
f6710aac | 176 | change: 'onInputTypeChange', |
f67e1cc0 TL |
177 | }, |
178 | 'textarea[name=serializedinfo]': { | |
179 | change: 'recomputeSerializedInfo', | |
f6710aac | 180 | enable: 'resetField', |
f67e1cc0 | 181 | }, |
f67e1cc0 | 182 | 'textfield': { |
f6710aac TL |
183 | disable: 'resetField', |
184 | }, | |
f67e1cc0 TL |
185 | }, |
186 | resetField: function(field) { | |
187 | field.reset(); | |
188 | }, | |
f67e1cc0 | 189 | onInputTypeChange: function(field, assistedInput) { |
5957f47f SR |
190 | let linkEditor = this.lookup('linkEditor'); |
191 | ||
192 | // this also clears all links | |
193 | linkEditor.setAllowNumberEdit(!assistedInput); | |
194 | ||
f67e1cc0 | 195 | if (!assistedInput) { |
5957f47f SR |
196 | linkEditor.setInfoText(); |
197 | linkEditor.setDefaultLinks(); | |
f67e1cc0 TL |
198 | } |
199 | }, | |
200 | recomputeSerializedInfo: function(field, value) { | |
5957f47f SR |
201 | let vm = this.getViewModel(); |
202 | ||
203 | let assistedEntryBox = this.lookup('assistedEntry'); | |
4081db07 | 204 | |
5957f47f SR |
205 | if (!assistedEntryBox.getValue()) { |
206 | // not in assisted entry mode, nothing to do | |
7788262a | 207 | vm.set('hasAssistedInfo', false); |
5957f47f SR |
208 | return; |
209 | } | |
210 | ||
211 | let linkEditor = this.lookup('linkEditor'); | |
212 | ||
213 | let jsons = Ext.util.Base64.decode(value); | |
214 | let joinInfo = Ext.JSON.decode(jsons, true); | |
f67e1cc0 | 215 | |
5957f47f | 216 | let info = { |
f67e1cc0 | 217 | fp: '', |
003e502f | 218 | ip: '', |
f6710aac | 219 | clusterName: '', |
f67e1cc0 TL |
220 | }; |
221 | ||
f67e1cc0 TL |
222 | if (!(joinInfo && joinInfo.totem)) { |
223 | field.valid = false; | |
5957f47f SR |
224 | linkEditor.setLinks([]); |
225 | linkEditor.setInfoText(); | |
7788262a | 226 | vm.set('hasAssistedInfo', false); |
f67e1cc0 | 227 | } else { |
5957f47f SR |
228 | let interfaces = joinInfo.totem.interface; |
229 | let links = Object.values(interfaces).map(iface => { | |
b1e7a7d3 SR |
230 | let linkNumber = iface.linknumber; |
231 | let peerLink; | |
232 | if (joinInfo.peerLinks) { | |
233 | peerLink = joinInfo.peerLinks[linkNumber]; | |
234 | } | |
5957f47f | 235 | return { |
b1e7a7d3 | 236 | number: linkNumber, |
5957f47f | 237 | value: '', |
b1e7a7d3 | 238 | text: peerLink ? Ext.String.format(gettext("peer's link address: {0}"), peerLink) : '', |
f6710aac | 239 | allowBlank: false, |
5957f47f SR |
240 | }; |
241 | }); | |
242 | ||
243 | linkEditor.setInfoText(); | |
244 | if (links.length == 1 && joinInfo.ring_addr !== undefined && | |
245 | joinInfo.ring_addr[0] === joinInfo.ipAddress) { | |
246 | ||
247 | links[0].allowBlank = true; | |
4ea2cac2 | 248 | links[0].emptyText = gettext("IP resolved by node's hostname"); |
9e3b13eb TL |
249 | } |
250 | ||
5957f47f SR |
251 | linkEditor.setLinks(links); |
252 | ||
f67e1cc0 TL |
253 | info = { |
254 | ip: joinInfo.ipAddress, | |
255 | fp: joinInfo.fingerprint, | |
f6710aac | 256 | clusterName: joinInfo.totem.cluster_name, |
f67e1cc0 | 257 | }; |
f67e1cc0 | 258 | field.valid = true; |
7788262a | 259 | vm.set('hasAssistedInfo', true); |
f67e1cc0 | 260 | } |
f67e1cc0 | 261 | vm.set('info', info); |
f6710aac | 262 | }, |
f67e1cc0 TL |
263 | }, |
264 | ||
fe13284e TL |
265 | submit: function() { |
266 | // joining may produce temporarily auth failures, ignore as long the task runs | |
267 | PVE.Utils.silenceAuthFailures = true; | |
268 | this.callParent(); | |
269 | }, | |
270 | ||
f67e1cc0 | 271 | taskDone: function(success) { |
fe13284e | 272 | delete PVE.Utils.silenceAuthFailures; |
f67e1cc0 | 273 | if (success) { |
44f193c3 TL |
274 | // reload always (if user wasn't faster), but wait a bit for pveproxy |
275 | Ext.defer(function() { | |
276 | window.location.reload(true); | |
277 | }, 5000); | |
f67e1cc0 TL |
278 | var txt = gettext('Cluster join task finished, node certificate may have changed, reload GUI!'); |
279 | // ensure user cannot do harm | |
280 | Ext.getBody().mask(txt, ['pve-static-mask']); | |
281 | // TaskView may hide above mask, so tell him directly | |
282 | Ext.Msg.show({ | |
283 | title: gettext('Join Task Finished'), | |
284 | icon: Ext.Msg.INFO, | |
f6710aac | 285 | msg: txt, |
f67e1cc0 | 286 | }); |
f67e1cc0 TL |
287 | } |
288 | }, | |
289 | ||
290 | items: [{ | |
291 | xtype: 'proxmoxcheckbox', | |
292 | reference: 'assistedEntry', | |
08e0aba8 | 293 | name: 'assistedEntry', |
5957f47f | 294 | itemId: 'assistedEntry', |
f67e1cc0 TL |
295 | submitValue: false, |
296 | value: true, | |
297 | autoEl: { | |
298 | tag: 'div', | |
f6710aac | 299 | 'data-qtip': gettext('Select if join information should be extracted from pasted cluster information, deselect for manual entering'), |
f67e1cc0 | 300 | }, |
f6710aac | 301 | boxLabel: gettext('Assisted join: Paste encoded cluster join information and enter password.'), |
f67e1cc0 TL |
302 | }, |
303 | { | |
304 | xtype: 'textarea', | |
305 | name: 'serializedinfo', | |
306 | submitValue: false, | |
307 | allowBlank: false, | |
308 | fieldLabel: gettext('Information'), | |
309 | emptyText: gettext('Paste encoded Cluster Information here'), | |
310 | validator: function(val) { | |
311 | return val === '' || this.valid || | |
312 | gettext('Does not seem like a valid encoded Cluster Information!'); | |
313 | }, | |
314 | bind: { | |
315 | disabled: '{!assistedEntry.checked}', | |
f6710aac | 316 | hidden: '{!assistedEntry.checked}', |
f67e1cc0 | 317 | }, |
f6710aac | 318 | value: '', |
f67e1cc0 TL |
319 | }, |
320 | { | |
5957f47f SR |
321 | xtype: 'panel', |
322 | width: 776, | |
323 | layout: { | |
324 | type: 'hbox', | |
f6710aac | 325 | align: 'center', |
5957f47f | 326 | }, |
7788262a TL |
327 | bind: { |
328 | hidden: '{!showClusterFields}', | |
329 | }, | |
5957f47f | 330 | items: [ |
f67e1cc0 TL |
331 | { |
332 | xtype: 'textfield', | |
5957f47f SR |
333 | flex: 1, |
334 | margin: '0 5px 0 0', | |
f67e1cc0 TL |
335 | fieldLabel: gettext('Peer Address'), |
336 | allowBlank: false, | |
337 | bind: { | |
338 | value: '{info.ip}', | |
9a9f1ac5 | 339 | readOnly: '{assistedEntry.checked}', |
f67e1cc0 | 340 | }, |
f6710aac | 341 | name: 'hostname', |
f67e1cc0 TL |
342 | }, |
343 | { | |
344 | xtype: 'textfield', | |
5957f47f SR |
345 | flex: 1, |
346 | margin: '0 0 10px 5px', | |
f67e1cc0 TL |
347 | inputType: 'password', |
348 | emptyText: gettext("Peer's root password"), | |
349 | fieldLabel: gettext('Password'), | |
350 | allowBlank: false, | |
f6710aac | 351 | name: 'password', |
f67e1cc0 | 352 | }, |
f6710aac | 353 | ], |
5957f47f SR |
354 | }, |
355 | { | |
356 | xtype: 'textfield', | |
357 | fieldLabel: gettext('Fingerprint'), | |
358 | allowBlank: false, | |
359 | bind: { | |
360 | value: '{info.fp}', | |
7788262a TL |
361 | readOnly: '{assistedEntry.checked}', |
362 | hidden: '{!showClusterFields}', | |
5957f47f | 363 | }, |
f6710aac | 364 | name: 'fingerprint', |
5957f47f SR |
365 | }, |
366 | { | |
367 | xtype: 'fieldcontainer', | |
41aa53ea | 368 | fieldLabel: gettext("Cluster Network"), |
4081db07 | 369 | bind: { |
7788262a | 370 | hidden: '{!showClusterFields}', |
4081db07 | 371 | }, |
5957f47f | 372 | items: [ |
f67e1cc0 | 373 | { |
5957f47f SR |
374 | xtype: 'pveCorosyncLinkEditor', |
375 | itemId: 'linkEditor', | |
376 | reference: 'linkEditor', | |
f6710aac | 377 | allowNumberEdit: false, |
5957f47f | 378 | }, |
f6710aac TL |
379 | ], |
380 | }], | |
f67e1cc0 | 381 | }); |