]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/dc/ClusterEdit.js
ui: cluster: code cleanups
[pve-manager.git] / www / manager6 / dc / ClusterEdit.js
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
16 onlineHelp: 'pvecm_create_cluster',
17
18 items: {
19 xtype: 'inputpanel',
20 items: [{
21 xtype: 'textfield',
22 fieldLabel: gettext('Cluster Name'),
23 allowBlank: false,
24 maxLength: 15,
25 name: 'clustername'
26 },
27 {
28 xtype: 'fieldcontainer',
29 fieldLabel: gettext("Cluster Network"),
30 items: [
31 {
32 xtype: 'pveCorosyncLinkEditor',
33 infoText: gettext("Multiple links are used as failover, lower numbers have higher priority."),
34 name: 'links'
35 },
36 ]
37 }]
38 }
39 });
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'),
73 cbind: {
74 value: '{joinInfo.ipAddress}',
75 },
76 editable: false,
77 },
78 {
79 xtype: 'textfield',
80 fieldLabel: gettext('Fingerprint'),
81 cbind: {
82 value: '{joinInfo.fingerprint}',
83 },
84 editable: false,
85 },
86 {
87 xtype: 'textarea',
88 inputId: 'pveSerializedClusterInfo',
89 fieldLabel: gettext('Join Information'),
90 grow: true,
91 cbind: {
92 joinInfo: '{joinInfo}',
93 },
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 });
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,
136 bind: {
137 submitText: '{submittxt}',
138 },
139 showTaskViewer: true,
140
141 onlineHelp: 'pvecm_join_node_to_cluster',
142
143 viewModel: {
144 parent: null,
145 data: {
146 info: {
147 fp: '',
148 ip: '',
149 clusterName: ''
150 }
151 },
152 formulas: {
153 submittxt: function(get) {
154 let cn = get('info.clusterName');
155 if (cn) {
156 return Ext.String.format(gettext('Join {0}'), `'${cn}'`);
157 }
158 return gettext('Join');
159 },
160 },
161 },
162
163 controller: {
164 xclass: 'Ext.app.ViewController',
165 control: {
166 '#': {
167 close: function() {
168 delete PVE.Utils.silenceAuthFailures;
169 }
170 },
171 'proxmoxcheckbox[name=assistedEntry]': {
172 change: 'onInputTypeChange'
173 },
174 'textarea[name=serializedinfo]': {
175 change: 'recomputeSerializedInfo',
176 enable: 'resetField'
177 },
178 'textfield': {
179 disable: 'resetField'
180 }
181 },
182 resetField: function(field) {
183 field.reset();
184 },
185 onInputTypeChange: function(field, assistedInput) {
186 let linkEditor = this.lookup('linkEditor');
187
188 // this also clears all links
189 linkEditor.setAllowNumberEdit(!assistedInput);
190
191 if (!assistedInput) {
192 linkEditor.setInfoText();
193 linkEditor.setDefaultLinks();
194 }
195 },
196 recomputeSerializedInfo: function(field, value) {
197 let vm = this.getViewModel();
198
199 let assistedEntryBox = this.lookup('assistedEntry');
200 if (!assistedEntryBox.getValue()) {
201 // not in assisted entry mode, nothing to do
202 return;
203 }
204
205 let linkEditor = this.lookup('linkEditor');
206
207 let jsons = Ext.util.Base64.decode(value);
208 let joinInfo = Ext.JSON.decode(jsons, true);
209
210 let info = {
211 fp: '',
212 ip: '',
213 clusterName: ''
214 };
215
216 if (!(joinInfo && joinInfo.totem)) {
217 field.valid = false;
218 linkEditor.setLinks([]);
219 linkEditor.setInfoText();
220 } else {
221 let interfaces = joinInfo.totem.interface;
222 let links = Object.values(interfaces).map(iface => {
223 let linkNumber = iface.linknumber;
224 let peerLink;
225 if (joinInfo.peerLinks) {
226 peerLink = joinInfo.peerLinks[linkNumber];
227 }
228 return {
229 number: linkNumber,
230 value: '',
231 text: peerLink ? Ext.String.format(gettext("peer's link address: {0}"), peerLink) : '',
232 allowBlank: false
233 };
234 });
235
236 linkEditor.setInfoText();
237 if (links.length == 1 && joinInfo.ring_addr !== undefined &&
238 joinInfo.ring_addr[0] === joinInfo.ipAddress) {
239
240 links[0].allowBlank = true;
241 links[0].emptyText = gettext("IP resolved by node's hostname");
242 }
243
244 linkEditor.setLinks(links);
245
246 info = {
247 ip: joinInfo.ipAddress,
248 fp: joinInfo.fingerprint,
249 clusterName: joinInfo.totem.cluster_name
250 };
251 field.valid = true;
252 }
253
254 vm.set('info', info);
255 }
256 },
257
258 submit: function() {
259 // joining may produce temporarily auth failures, ignore as long the task runs
260 PVE.Utils.silenceAuthFailures = true;
261 this.callParent();
262 },
263
264 taskDone: function(success) {
265 delete PVE.Utils.silenceAuthFailures;
266 if (success) {
267 // reload always (if user wasn't faster), but wait a bit for pveproxy
268 Ext.defer(function() {
269 window.location.reload(true);
270 }, 5000);
271 var txt = gettext('Cluster join task finished, node certificate may have changed, reload GUI!');
272 // ensure user cannot do harm
273 Ext.getBody().mask(txt, ['pve-static-mask']);
274 // TaskView may hide above mask, so tell him directly
275 Ext.Msg.show({
276 title: gettext('Join Task Finished'),
277 icon: Ext.Msg.INFO,
278 msg: txt
279 });
280 }
281 },
282
283 items: [{
284 xtype: 'proxmoxcheckbox',
285 reference: 'assistedEntry',
286 name: 'assistedEntry',
287 itemId: 'assistedEntry',
288 submitValue: false,
289 value: true,
290 autoEl: {
291 tag: 'div',
292 'data-qtip': gettext('Select if join information should be extracted from pasted cluster information, deselect for manual entering')
293 },
294 boxLabel: gettext('Assisted join: Paste encoded cluster join information and enter password.')
295 },
296 {
297 xtype: 'textarea',
298 name: 'serializedinfo',
299 submitValue: false,
300 allowBlank: false,
301 fieldLabel: gettext('Information'),
302 emptyText: gettext('Paste encoded Cluster Information here'),
303 validator: function(val) {
304 return val === '' || this.valid ||
305 gettext('Does not seem like a valid encoded Cluster Information!');
306 },
307 bind: {
308 disabled: '{!assistedEntry.checked}',
309 hidden: '{!assistedEntry.checked}'
310 },
311 value: ''
312 },
313 {
314 xtype: 'panel',
315 width: 776,
316 layout: {
317 type: 'hbox',
318 align: 'center'
319 },
320 items: [
321 {
322 xtype: 'textfield',
323 flex: 1,
324 margin: '0 5px 0 0',
325 fieldLabel: gettext('Peer Address'),
326 allowBlank: false,
327 bind: {
328 value: '{info.ip}',
329 readOnly: '{assistedEntry.checked}',
330 },
331 name: 'hostname'
332 },
333 {
334 xtype: 'textfield',
335 flex: 1,
336 margin: '0 0 10px 5px',
337 inputType: 'password',
338 emptyText: gettext("Peer's root password"),
339 fieldLabel: gettext('Password'),
340 allowBlank: false,
341 name: 'password'
342 },
343 ]
344 },
345 {
346 xtype: 'textfield',
347 fieldLabel: gettext('Fingerprint'),
348 allowBlank: false,
349 bind: {
350 value: '{info.fp}',
351 readOnly: '{assistedEntry.checked}'
352 },
353 name: 'fingerprint'
354 },
355 {
356 xtype: 'fieldcontainer',
357 fieldLabel: gettext("Cluster Network"),
358 items: [
359 {
360 xtype: 'pveCorosyncLinkEditor',
361 itemId: 'linkEditor',
362 reference: 'linkEditor',
363 allowNumberEdit: false
364 },
365 ]
366 }]
367 });