pve-manager/www/manager6/dc/ClusterEdit.js
Thomas Lamprecht 9e3b13eb7f ui: cluster: require ring0_addr if joinee's ring and node addr differ
Pass the ring addresses from the node we use to join a cluster with
the encoded cluster info. Use it to determine if the nodes public
address (i.e., the onei t's node name resolves to) differs from it'S
ring0 address, if that's the case and the cluster was created with
PVE tooling then we know that it's highly probable that the ring0 is
on another network than the public net, so require the ring0_addrss
field in this case. If we have a false positive, it's not to bad
here.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2018-11-28 12:39:40 +01:00

332 lines
7.4 KiB
JavaScript

/*jslint confusion: true*/
Ext.define('PVE.ClusterCreateWindow', {
extend: 'Proxmox.window.Edit',
xtype: 'pveClusterCreateWindow',
title: gettext('Create Cluster'),
width: 600,
method: 'POST',
url: '/cluster/config',
isCreate: true,
subject: gettext('Cluster'),
showTaskViewer: true,
items: [
{
xtype: 'textfield',
fieldLabel: gettext('Cluster Name'),
allowBlank: false,
name: 'clustername'
},
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('Ring 0 Address'),
emptyText: gettext("Optional, defaults to IP resolved by node's hostname"),
name: 'ring0_addr',
skipEmptyText: true
}
// TODO: for advanced options: ring1_addr
]
});
Ext.define('PVE.ClusterInfoWindow', {
extend: 'Ext.window.Window',
xtype: 'pveClusterInfoWindow',
mixins: ['Proxmox.Mixin.CBind'],
width: 800,
modal: true,
resizable: false,
title: gettext('Cluster Join Information'),
joinInfo: {
ipAddress: undefined,
fingerprint: undefined,
totem: {}
},
items: [
{
xtype: 'component',
border: false,
padding: '10 10 10 10',
html: gettext("Copy the Join Information here and use it on the node you want to add.")
},
{
xtype: 'container',
layout: 'form',
border: false,
padding: '0 10 10 10',
items: [
{
xtype: 'textfield',
fieldLabel: gettext('IP Address'),
cbind: { value: '{joinInfo.ipAddress}' },
editable: false
},
{
xtype: 'textfield',
fieldLabel: gettext('Fingerprint'),
cbind: { value: '{joinInfo.fingerprint}' },
editable: false
},
{
xtype: 'textarea',
inputId: 'pveSerializedClusterInfo',
fieldLabel: gettext('Join Information'),
grow: true,
cbind: { joinInfo: '{joinInfo}' },
editable: false,
listeners: {
afterrender: function(field) {
if (!field.joinInfo) {
return;
}
var jsons = Ext.JSON.encode(field.joinInfo);
var base64s = Ext.util.Base64.encode(jsons);
field.setValue(base64s);
}
}
}
]
}
],
dockedItems: [{
dock: 'bottom',
xtype: 'toolbar',
items: [{
xtype: 'button',
handler: function(b) {
var el = document.getElementById('pveSerializedClusterInfo');
el.select();
document.execCommand("copy");
},
text: gettext('Copy Information')
}]
}]
});
Ext.define('PVE.ClusterJoinNodeWindow', {
extend: 'Proxmox.window.Edit',
xtype: 'pveClusterJoinNodeWindow',
title: gettext('Cluster Join'),
width: 800,
method: 'POST',
url: '/cluster/config/join',
defaultFocus: 'textarea[name=serializedinfo]',
isCreate: true,
submitText: gettext('Join'),
showTaskViewer: true,
onlineHelp: 'chapter_pvecm',
viewModel: {
parent: null,
data: {
info: {
fp: '',
ip: '',
ring0Needed: false,
ring1Possible: false,
ring1Needed: false
}
},
formulas: {
ring0EmptyText: function(get) {
if (get('info.ring0Needed')) {
return gettext("Cannot use default address safely");
} else {
return gettext("Default: IP resolved by node's hostname");
}
}
}
},
controller: {
xclass: 'Ext.app.ViewController',
control: {
'#': {
close: function() {
delete PVE.Utils.silenceAuthFailures;
}
},
'proxmoxcheckbox[name=assistedEntry]': {
change: 'onInputTypeChange'
},
'textarea[name=serializedinfo]': {
change: 'recomputeSerializedInfo',
enable: 'resetField'
},
'proxmoxtextfield[name=ring1_addr]': {
enable: 'ring1Needed'
},
'textfield': {
disable: 'resetField'
}
},
resetField: function(field) {
field.reset();
},
ring1Needed: function(f) {
var vm = this.getViewModel();
f.allowBlank = !vm.get('info.ring1Needed');
},
onInputTypeChange: function(field, assistedInput) {
var vm = this.getViewModel();
if (!assistedInput) {
vm.set('info.ring1Possible', true);
}
},
recomputeSerializedInfo: function(field, value) {
var vm = this.getViewModel();
var jsons = Ext.util.Base64.decode(value);
var joinInfo = Ext.JSON.decode(jsons, true);
var info = {
fp: '',
ring1Needed: false,
ring1Possible: false,
ip: ''
};
var totem = {};
if (!(joinInfo && joinInfo.totem)) {
field.valid = false;
} else {
var ring0Needed = false;
if (joinInfo.ring_addr !== undefined) {
ring0Needed = joinInfo.ring_addr[0] !== joinInfo.ipAddress;
}
info = {
ip: joinInfo.ipAddress,
fp: joinInfo.fingerprint,
ring0Needed: ring0Needed,
ring1Possible: !!joinInfo.totem['interface']['1'],
ring1Needed: !!joinInfo.totem['interface']['1']
};
totem = joinInfo.totem;
field.valid = true;
}
vm.set('info', info);
}
},
submit: function() {
// joining may produce temporarily auth failures, ignore as long the task runs
PVE.Utils.silenceAuthFailures = true;
this.callParent();
},
taskDone: function(success) {
delete PVE.Utils.silenceAuthFailures;
if (success) {
var txt = gettext('Cluster join task finished, node certificate may have changed, reload GUI!');
// ensure user cannot do harm
Ext.getBody().mask(txt, ['pve-static-mask']);
// TaskView may hide above mask, so tell him directly
Ext.Msg.show({
title: gettext('Join Task Finished'),
icon: Ext.Msg.INFO,
msg: txt
});
// reload always (if user wasn't faster), but wait a bit for pveproxy
Ext.defer(function() {
window.location.reload(true);
}, 5000);
}
},
items: [{
xtype: 'proxmoxcheckbox',
reference: 'assistedEntry',
name: 'assistedEntry',
submitValue: false,
value: true,
autoEl: {
tag: 'div',
'data-qtip': gettext('Select if join information should be extracted from pasted cluster information, deselect for manual entering')
},
boxLabel: gettext('Assisted join: Paste encoded cluster join information and enter password.')
},
{
xtype: 'textarea',
name: 'serializedinfo',
submitValue: false,
allowBlank: false,
fieldLabel: gettext('Information'),
emptyText: gettext('Paste encoded Cluster Information here'),
validator: function(val) {
return val === '' || this.valid ||
gettext('Does not seem like a valid encoded Cluster Information!');
},
bind: {
disabled: '{!assistedEntry.checked}',
hidden: '{!assistedEntry.checked}'
},
value: ''
},
{
xtype: 'inputpanel',
column1: [
{
xtype: 'textfield',
fieldLabel: gettext('Peer Address'),
allowBlank: false,
bind: {
value: '{info.ip}',
readOnly: '{assistedEntry.checked}'
},
name: 'hostname'
},
{
xtype: 'textfield',
inputType: 'password',
emptyText: gettext("Peer's root password"),
fieldLabel: gettext('Password'),
allowBlank: false,
name: 'password'
}
],
column2: [
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('Corosync Ring 0'),
bind: {
emptyText: '{ring0EmptyText}',
allowBlank: '{!info.ring0Needed}'
},
skipEmptyText: true,
name: 'ring0_addr'
},
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('Corosync Ring 1'),
skipEmptyText: true,
bind: {
disabled: '{!info.ring1Possible}'
},
name: 'ring1_addr'
}
],
columnB: [
{
xtype: 'textfield',
fieldLabel: gettext('Fingerprint'),
allowBlank: false,
bind: {
value: '{info.fp}',
readOnly: '{assistedEntry.checked}'
},
name: 'fingerprint'
}
]
}]
});