pve-manager/www/manager6/dc/Cluster.js
Stefan Reiter b1e7a7d3b7 gui/cluster: add structured peerLinks to join info
Instead of the old 'ring_addr' property (which is kept for
compatibility), we also encode the link numbers into the new peerLinks
structure. This allows us to display which IP is assigned to which link
on the cluster in the join dialog, helping a user identify which link
should receive which interface on the new node.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
2020-05-04 18:44:15 +02:00

347 lines
7.4 KiB
JavaScript

/*jslint confusion: true*/
Ext.define('pve-cluster-nodes', {
extend: 'Ext.data.Model',
fields: [
'node', { type: 'integer', name: 'nodeid' }, 'ring0_addr', 'ring1_addr',
{ type: 'integer', name: 'quorum_votes' }
],
proxy: {
type: 'proxmox',
url: "/api2/json/cluster/config/nodes"
},
idProperty: 'nodeid'
});
Ext.define('pve-cluster-info', {
extend: 'Ext.data.Model',
proxy: {
type: 'proxmox',
url: "/api2/json/cluster/config/join"
}
});
Ext.define('PVE.ClusterAdministration', {
extend: 'Ext.panel.Panel',
xtype: 'pveClusterAdministration',
title: gettext('Cluster Administration'),
onlineHelp: 'chapter_pvecm',
border: false,
defaults: { border: false },
viewModel: {
parent: null,
data: {
totem: {},
nodelist: [],
preferred_node: {
name: '',
fp: '',
addr: ''
},
isInCluster: false,
nodecount: 0
}
},
items: [
{
xtype: 'panel',
title: gettext('Cluster Information'),
controller: {
xclass: 'Ext.app.ViewController',
init: function(view) {
view.store = Ext.create('Proxmox.data.UpdateStore', {
autoStart: true,
interval: 15 * 1000,
storeid: 'pve-cluster-info',
model: 'pve-cluster-info'
});
view.store.on('load', this.onLoad, this);
view.on('destroy', view.store.stopUpdate);
},
onLoad: function(store, records, success) {
var vm = this.getViewModel();
if (!success || !records || !records[0].data) {
vm.set('totem', {});
vm.set('isInCluster', false);
vm.set('nodelist', []);
vm.set('preferred_node', {
name: '',
addr: '',
fp: ''
});
return;
}
var data = records[0].data;
vm.set('totem', data.totem);
vm.set('isInCluster', !!data.totem.cluster_name);
vm.set('nodelist', data.nodelist);
var nodeinfo = Ext.Array.findBy(data.nodelist, function (el) {
return el.name === data.preferred_node;
});
let links = {};
let ring_addr = [];
PVE.Utils.forEachCorosyncLink(nodeinfo, (num, link) => {
links[num] = link;
ring_addr.push(link);
});
vm.set('preferred_node', {
name: data.preferred_node,
addr: nodeinfo.pve_addr,
peerLinks: links,
ring_addr: ring_addr,
fp: nodeinfo.pve_fp
});
},
onCreate: function() {
var view = this.getView();
view.store.stopUpdate();
var win = Ext.create('PVE.ClusterCreateWindow', {
autoShow: true,
listeners: {
destroy: function() {
view.store.startUpdate();
}
}
});
},
onClusterInfo: function() {
var vm = this.getViewModel();
var win = Ext.create('PVE.ClusterInfoWindow', {
joinInfo: {
ipAddress: vm.get('preferred_node.addr'),
fingerprint: vm.get('preferred_node.fp'),
peerLinks: vm.get('preferred_node.peerLinks'),
ring_addr: vm.get('preferred_node.ring_addr'),
totem: vm.get('totem')
}
});
win.show();
},
onJoin: function() {
var view = this.getView();
view.store.stopUpdate();
var win = Ext.create('PVE.ClusterJoinNodeWindow', {
autoShow: true,
listeners: {
destroy: function() {
view.store.startUpdate();
}
}
});
}
},
tbar: [
{
text: gettext('Create Cluster'),
reference: 'createButton',
handler: 'onCreate',
bind: {
disabled: '{isInCluster}'
}
},
{
text: gettext('Join Information'),
reference: 'addButton',
handler: 'onClusterInfo',
bind: {
disabled: '{!isInCluster}'
}
},
{
text: gettext('Join Cluster'),
reference: 'joinButton',
handler: 'onJoin',
bind: {
disabled: '{isInCluster}'
}
}
],
layout: 'hbox',
bodyPadding: 5,
items: [
{
xtype: 'displayfield',
fieldLabel: gettext('Cluster Name'),
bind: {
value: '{totem.cluster_name}',
hidden: '{!isInCluster}'
},
flex: 1
},
{
xtype: 'displayfield',
fieldLabel: gettext('Config Version'),
bind: {
value: '{totem.config_version}',
hidden: '{!isInCluster}'
},
flex: 1
},
{
xtype: 'displayfield',
fieldLabel: gettext('Number of Nodes'),
labelWidth: 120,
bind: {
value: '{nodecount}',
hidden: '{!isInCluster}'
},
flex: 1
},
{
xtype: 'displayfield',
value: gettext('Standalone node - no cluster defined'),
bind: {
hidden: '{isInCluster}'
},
flex: 1
}
]
},
{
xtype: 'grid',
title: gettext('Cluster Nodes'),
autoScroll: true,
enableColumnHide: false,
controller: {
xclass: 'Ext.app.ViewController',
init: function(view) {
view.rstore = Ext.create('Proxmox.data.UpdateStore', {
autoLoad: true,
xtype: 'update',
interval: 5 * 1000,
autoStart: true,
storeid: 'pve-cluster-nodes',
model: 'pve-cluster-nodes'
});
view.setStore(Ext.create('Proxmox.data.DiffStore', {
rstore: view.rstore,
sorters: {
property: 'nodeid',
order: 'DESC'
}
}));
Proxmox.Utils.monStoreErrors(view, view.rstore);
view.rstore.on('load', this.onLoad, this);
view.on('destroy', view.rstore.stopUpdate);
},
onLoad: function(store, records, success) {
var view = this.getView();
var vm = this.getViewModel();
if (!success || !records || !records.length) {
vm.set('nodecount', 0);
return;
}
vm.set('nodecount', records.length);
// show/hide columns according to used links
var linkIndex = view.columns.length;
var columns = Ext.each(view.columns, (col, i) => {
if (col.linkNumber !== undefined) {
col.setHidden(true);
// save offset at which link columns start, so we
// can address them directly below
if (i < linkIndex) {
linkIndex = i;
}
}
});
PVE.Utils.forEachCorosyncLink(records[0].data,
(linknum, val) => {
if (linknum > 7) {
return;
}
view.columns[linkIndex+linknum].setHidden(false);
}
);
}
},
columns: {
items: [
{
header: gettext('Nodename'),
hidden: false,
dataIndex: 'name'
},
{
header: gettext('ID'),
minWidth: 100,
width: 100,
flex: 0,
hidden: false,
dataIndex: 'nodeid'
},
{
header: gettext('Votes'),
minWidth: 100,
width: 100,
flex: 0,
hidden: false,
dataIndex: 'quorum_votes'
},
{
header: Ext.String.format(gettext('Link {0}'), 0),
dataIndex: 'ring0_addr',
linkNumber: 0
},
{
header: Ext.String.format(gettext('Link {0}'), 1),
dataIndex: 'ring1_addr',
linkNumber: 1
},
{
header: Ext.String.format(gettext('Link {0}'), 2),
dataIndex: 'ring2_addr',
linkNumber: 2
},
{
header: Ext.String.format(gettext('Link {0}'), 3),
dataIndex: 'ring3_addr',
linkNumber: 3
},
{
header: Ext.String.format(gettext('Link {0}'), 4),
dataIndex: 'ring4_addr',
linkNumber: 4
},
{
header: Ext.String.format(gettext('Link {0}'), 5),
dataIndex: 'ring5_addr',
linkNumber: 5
},
{
header: Ext.String.format(gettext('Link {0}'), 6),
dataIndex: 'ring6_addr',
linkNumber: 6
},
{
header: Ext.String.format(gettext('Link {0}'), 7),
dataIndex: 'ring7_addr',
linkNumber: 7
}
],
defaults: {
flex: 1,
hidden: true,
minWidth: 150
}
}
}
]
});