]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/dc/ClusterEdit.js
ui: clusterjoin: fix cluster network field visibility
[pve-manager.git] / www / manager6 / dc / ClusterEdit.js
CommitLineData
8ad2b3a1
TL
1/*jslint confusion: true*/
2Ext.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
41Ext.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
124Ext.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});