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