mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-08-15 08:31:49 +00:00
add Datacenter summary
This commit is contained in:
parent
ca457ac1ef
commit
a0af013278
@ -3,6 +3,8 @@ package PVE::API2::Cluster;
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
|
use XML::Parser;
|
||||||
|
|
||||||
use PVE::SafeSyslog;
|
use PVE::SafeSyslog;
|
||||||
use PVE::Tools qw(extract_param);
|
use PVE::Tools qw(extract_param);
|
||||||
use PVE::INotify;
|
use PVE::INotify;
|
||||||
@ -66,6 +68,7 @@ __PACKAGE__->register_method ({
|
|||||||
{ name => 'tasks' },
|
{ name => 'tasks' },
|
||||||
{ name => 'backup' },
|
{ name => 'backup' },
|
||||||
{ name => 'ha' },
|
{ name => 'ha' },
|
||||||
|
{ name => 'status' },
|
||||||
];
|
];
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@ -347,4 +350,134 @@ __PACKAGE__->register_method({
|
|||||||
return undef;
|
return undef;
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
my $parse_clustat = sub {
|
||||||
|
my ($clinfo, $members, $nodename, $raw) = @_;
|
||||||
|
|
||||||
|
my $createNode = sub {
|
||||||
|
my ($expat, $tag, %attrib) = @_;
|
||||||
|
my $node = { type => $tag, %attrib };
|
||||||
|
|
||||||
|
if ($tag eq 'node') {
|
||||||
|
my $name = $node->{name};
|
||||||
|
return if !$name; # just to be sure
|
||||||
|
|
||||||
|
foreach my $key (qw(estranged local qdisk rgmanager rgmanager_master state)) {
|
||||||
|
$node->{$key} = int($node->{$key}) if defined($node->{$key});
|
||||||
|
}
|
||||||
|
$node->{nodeid} = hex($node->{nodeid}) if defined($node->{nodeid});
|
||||||
|
|
||||||
|
# unique ID for GUI
|
||||||
|
$node->{id} = "node/$node->{name}";
|
||||||
|
|
||||||
|
my $pmxcfs = 0;
|
||||||
|
if (!$members) { # no cluster
|
||||||
|
if ($name eq $nodename) {
|
||||||
|
$pmxcfs = ($clinfo && $clinfo->{version}) ? 1 : 0; # pmxcfs online ?
|
||||||
|
}
|
||||||
|
} elsif ($members->{$name}) {
|
||||||
|
$pmxcfs = $members->{$name}->{online} ? 1 : 0
|
||||||
|
}
|
||||||
|
$node->{pmxcfs} = $pmxcfs;
|
||||||
|
|
||||||
|
if ($members && $members->{$name}) {
|
||||||
|
if (my $ip = $members->{$name}->{ip}) {
|
||||||
|
$node->{ip} = $ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elsif ($tag eq 'group') {
|
||||||
|
my $name = $node->{name};
|
||||||
|
return if !$name; # just to be sure
|
||||||
|
# unique ID for GUI
|
||||||
|
$node->{id} = "group/$node->{name}";
|
||||||
|
} else {
|
||||||
|
$node->{id} = $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $node;
|
||||||
|
};
|
||||||
|
|
||||||
|
my $extract_tags = {
|
||||||
|
cluster => 1,
|
||||||
|
quorum => 1,
|
||||||
|
node => 1,
|
||||||
|
group => 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
my $handlers = {
|
||||||
|
Init => sub {
|
||||||
|
my $expat = shift;
|
||||||
|
$expat->{NodeList} = [];
|
||||||
|
},
|
||||||
|
Final => sub {
|
||||||
|
my $expat = shift;
|
||||||
|
$expat->{NodeList};
|
||||||
|
},
|
||||||
|
Start => sub {
|
||||||
|
my $expat = shift;
|
||||||
|
my $tag = shift;
|
||||||
|
if ($extract_tags->{$tag}) {
|
||||||
|
my $node = &$createNode($expat, $tag, @_);
|
||||||
|
push @{$expat->{NodeList}}, $node;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
my $data = [];
|
||||||
|
if ($raw) {
|
||||||
|
my $parser = new XML::Parser(Handlers => $handlers);
|
||||||
|
$data = $parser->parse($raw);
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
};
|
||||||
|
|
||||||
|
__PACKAGE__->register_method({
|
||||||
|
name => 'get_status',
|
||||||
|
path => 'status',
|
||||||
|
method => 'GET',
|
||||||
|
description => "Get cluster status informations.",
|
||||||
|
permissions => { user => 'all' },
|
||||||
|
protected => 1,
|
||||||
|
parameters => {
|
||||||
|
additionalProperties => 0,
|
||||||
|
properties => {},
|
||||||
|
},
|
||||||
|
returns => {
|
||||||
|
type => 'array',
|
||||||
|
items => {
|
||||||
|
type => "object",
|
||||||
|
properties => {
|
||||||
|
type => {
|
||||||
|
type => 'string'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
code => sub {
|
||||||
|
my ($param) = @_;
|
||||||
|
|
||||||
|
# we also add info from pmxcfs
|
||||||
|
my $clinfo = PVE::Cluster::get_clinfo();
|
||||||
|
my $members = PVE::Cluster::get_members();
|
||||||
|
my $nodename = PVE::INotify::nodename();
|
||||||
|
|
||||||
|
if ($members) {
|
||||||
|
my $cmd = ['clustat', '-x'];
|
||||||
|
my $out = '';
|
||||||
|
PVE::Tools::run_command($cmd, outfunc => sub { $out .= shift; });
|
||||||
|
return &$parse_clustat($clinfo, $members, $nodename, $out);
|
||||||
|
} else {
|
||||||
|
# fake entry for local node if no cluster defined
|
||||||
|
my $pmxcfs = ($clinfo && $clinfo->{version}) ? 1 : 0; # pmxcfs online ?
|
||||||
|
return [{
|
||||||
|
type => 'node',
|
||||||
|
id => "node/$nodename",
|
||||||
|
name => $nodename,
|
||||||
|
'local' => 1,
|
||||||
|
nodeid => 0,
|
||||||
|
pmxcfs => $pmxcfs,
|
||||||
|
state => 1
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
6
debian/changelog.Debian
vendored
6
debian/changelog.Debian
vendored
@ -1,3 +1,9 @@
|
|||||||
|
pve-manager (2.0-18) unstable; urgency=low
|
||||||
|
|
||||||
|
* add Datacenter summary
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Fri, 23 Dec 2011 10:39:34 +0100
|
||||||
|
|
||||||
pve-manager (2.0-17) unstable; urgency=low
|
pve-manager (2.0-17) unstable; urgency=low
|
||||||
|
|
||||||
* new HA configuration panel
|
* new HA configuration panel
|
||||||
|
2
debian/control.in
vendored
2
debian/control.in
vendored
@ -3,7 +3,7 @@ Version: @VERSION@-@PACKAGERELEASE@
|
|||||||
Section: admin
|
Section: admin
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: perl5, libtimedate-perl, apache2-mpm-prefork, libauthen-pam-perl, libintl-perl, rsync, libapache2-request-perl, libjson-perl, libdigest-sha1-perl, liblockfile-simple-perl, vncterm, qemu-server (>= 1.1-1), libwww-perl, wget, libnet-dns-perl, vlan, ifenslave-2.6 (>= 1.1.0-10), liblinux-inotify2-perl, debconf (>= 0.5) | debconf-2.0, netcat-traditional, pve-cluster, libpve-common-perl, libpve-storage-perl, libterm-readline-gnu-perl, libpve-access-control, libio-socket-ssl-perl, libfilesys-df-perl, libfile-readbackwards-perl, libfile-sync-perl, redhat-cluster-pve, cstream, mail-transport-agent
|
Depends: perl5, libtimedate-perl, apache2-mpm-prefork, libauthen-pam-perl, libintl-perl, rsync, libapache2-request-perl, libjson-perl, libdigest-sha1-perl, liblockfile-simple-perl, vncterm, qemu-server (>= 1.1-1), libwww-perl, wget, libnet-dns-perl, vlan, ifenslave-2.6 (>= 1.1.0-10), liblinux-inotify2-perl, debconf (>= 0.5) | debconf-2.0, netcat-traditional, pve-cluster, libpve-common-perl, libpve-storage-perl, libterm-readline-gnu-perl, libpve-access-control, libio-socket-ssl-perl, libfilesys-df-perl, libfile-readbackwards-perl, libfile-sync-perl, redhat-cluster-pve, cstream, mail-transport-agent, libxml-parser-perl
|
||||||
Conflicts: netcat-openbsd, vzdump
|
Conflicts: netcat-openbsd, vzdump
|
||||||
Replaces: vzdump
|
Replaces: vzdump
|
||||||
Provides: vzdump
|
Provides: vzdump
|
||||||
|
@ -2,7 +2,7 @@ RELEASE=2.0
|
|||||||
|
|
||||||
VERSION=2.0
|
VERSION=2.0
|
||||||
PACKAGE=pve-manager
|
PACKAGE=pve-manager
|
||||||
PACKAGERELEASE=17
|
PACKAGERELEASE=18
|
||||||
|
|
||||||
BINDIR=${DESTDIR}/usr/bin
|
BINDIR=${DESTDIR}/usr/bin
|
||||||
PERLLIBDIR=${DESTDIR}/usr/share/perl5
|
PERLLIBDIR=${DESTDIR}/usr/share/perl5
|
||||||
|
@ -110,6 +110,7 @@ JSSRC= \
|
|||||||
storage/NFSEdit.js \
|
storage/NFSEdit.js \
|
||||||
storage/IScsiEdit.js \
|
storage/IScsiEdit.js \
|
||||||
storage/LVMEdit.js \
|
storage/LVMEdit.js \
|
||||||
|
dc/Summary.js \
|
||||||
dc/OptionView.js \
|
dc/OptionView.js \
|
||||||
dc/StorageView.js \
|
dc/StorageView.js \
|
||||||
dc/UserEdit.js \
|
dc/UserEdit.js \
|
||||||
|
@ -9,6 +9,11 @@ Ext.define('PVE.dc.Config', {
|
|||||||
title: gettext("Datacenter"),
|
title: gettext("Datacenter"),
|
||||||
hstateid: 'dctab',
|
hstateid: 'dctab',
|
||||||
items: [
|
items: [
|
||||||
|
{
|
||||||
|
title: gettext('Summary'),
|
||||||
|
xtype: 'pveDcSummary',
|
||||||
|
itemId: 'summary'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
xtype: 'pveDcOptionView',
|
xtype: 'pveDcOptionView',
|
||||||
title: gettext('Options'),
|
title: gettext('Options'),
|
||||||
|
261
www/manager/dc/Summary.js
Normal file
261
www/manager/dc/Summary.js
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
Ext.define('PVE.dc.NodeView', {
|
||||||
|
extend: 'Ext.grid.GridPanel',
|
||||||
|
|
||||||
|
alias: ['widget.pveDcNodeView'],
|
||||||
|
|
||||||
|
initComponent : function() {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
var rstore = Ext.create('PVE.data.UpdateStore', {
|
||||||
|
interval: 3000,
|
||||||
|
storeid: 'pve-dc-nodes',
|
||||||
|
model: 'pve-dc-nodes',
|
||||||
|
proxy: {
|
||||||
|
type: 'pve',
|
||||||
|
url: "/api2/json/cluster/status"
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
property: 'type',
|
||||||
|
value : 'node'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var store = Ext.create('PVE.data.DiffStore', { rstore: rstore });
|
||||||
|
|
||||||
|
Ext.apply(me, {
|
||||||
|
store: store,
|
||||||
|
stateful: false,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
header: gettext('Name'),
|
||||||
|
width: 200,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'ID',
|
||||||
|
width: 50,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'nodeid'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Online'),
|
||||||
|
width: 100,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'state',
|
||||||
|
renderer: PVE.Utils.format_boolean
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Estranged'),
|
||||||
|
width: 100,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'estranged',
|
||||||
|
renderer: PVE.Utils.format_boolean
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Server Address'),
|
||||||
|
width: 100,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'ip'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Services'),
|
||||||
|
flex: 1,
|
||||||
|
width: 80,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'pmxcfs',
|
||||||
|
renderer: function(value, metaData, record) {
|
||||||
|
var list = [];
|
||||||
|
var data = record.data;
|
||||||
|
if (data) {
|
||||||
|
if (data.pmxcfs) {
|
||||||
|
list.push('PVECluster');
|
||||||
|
}
|
||||||
|
if (data.rgmanager) {
|
||||||
|
list.push('RGManager');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return list.join(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
listeners: {
|
||||||
|
show: rstore.startUpdate,
|
||||||
|
hide: rstore.stopUpdate,
|
||||||
|
destroy: rstore.stopUpdate
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
me.callParent();
|
||||||
|
}
|
||||||
|
}, function() {
|
||||||
|
|
||||||
|
Ext.define('pve-dc-nodes', {
|
||||||
|
extend: 'Ext.data.Model',
|
||||||
|
fields: [ 'id', 'type', 'name', 'state', 'nodeid', 'ip',
|
||||||
|
'pmxcfs', 'rgmanager', 'estranged' ],
|
||||||
|
idProperty: 'id'
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Ext.define('PVE.dc.HAServiceView', {
|
||||||
|
extend: 'Ext.grid.GridPanel',
|
||||||
|
|
||||||
|
alias: ['widget.pveHaServiceView'],
|
||||||
|
|
||||||
|
initComponent : function() {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
var rstore = Ext.create('PVE.data.UpdateStore', {
|
||||||
|
interval: 3000,
|
||||||
|
storeid: 'pve-ha-services',
|
||||||
|
model: 'pve-ha-services',
|
||||||
|
proxy: {
|
||||||
|
type: 'pve',
|
||||||
|
url: "/api2/json/cluster/status"
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
property: 'type',
|
||||||
|
value : 'group'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var store = Ext.create('PVE.data.DiffStore', { rstore: rstore });
|
||||||
|
|
||||||
|
var noClusterText = gettext("Standalone node - no cluster defined");
|
||||||
|
var status = Ext.create('Ext.Component', {
|
||||||
|
padding: 2,
|
||||||
|
html: ' ',
|
||||||
|
dock: 'bottom'
|
||||||
|
});
|
||||||
|
|
||||||
|
Ext.apply(me, {
|
||||||
|
store: store,
|
||||||
|
stateful: false,
|
||||||
|
//tbar: [ 'start', 'stop' ],
|
||||||
|
bbar: [ status ],
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
header: gettext('Name'),
|
||||||
|
flex: 1,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'name'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Owner'),
|
||||||
|
flex: 1,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'owner'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('State'),
|
||||||
|
width: 80,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'state_str'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Restarts'),
|
||||||
|
width: 80,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'restarts'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Last transition'),
|
||||||
|
width: 200,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'last_transition'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Last owner'),
|
||||||
|
flex: 1,
|
||||||
|
sortable: true,
|
||||||
|
dataIndex: 'last_owner'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
listeners: {
|
||||||
|
show: rstore.startUpdate,
|
||||||
|
hide: rstore.stopUpdate,
|
||||||
|
destroy: rstore.stopUpdate
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
me.callParent();
|
||||||
|
|
||||||
|
rstore.on('load', function(s, records, success) {
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cluster_rec = rstore.getById('cluster');
|
||||||
|
var quorum_rec = rstore.getById('quorum');
|
||||||
|
|
||||||
|
if (!(cluster_rec && quorum_rec)) {
|
||||||
|
status.update(noClusterText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cluster_raw = cluster_rec.raw;
|
||||||
|
var quorum_raw = quorum_rec.raw;
|
||||||
|
if (!(cluster_raw && quorum_raw)) {
|
||||||
|
status.update(noClusterText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status.update("Quorate: " + PVE.Utils.format_boolean(quorum_raw.quorate));
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}, function() {
|
||||||
|
|
||||||
|
Ext.define('pve-ha-services', {
|
||||||
|
extend: 'Ext.data.Model',
|
||||||
|
fields: [ 'id', 'type', 'name', 'owner', 'last_owner', 'state_str', 'restarts',
|
||||||
|
{ name: 'last_transition', type: 'date', dateFormat: 'timestamp'}
|
||||||
|
],
|
||||||
|
idProperty: 'id'
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Ext.define('PVE.dc.Summary', {
|
||||||
|
extend: 'Ext.panel.Panel',
|
||||||
|
alias: 'widget.pveDcSummary',
|
||||||
|
|
||||||
|
initComponent: function() {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
|
var hagrid = Ext.create('PVE.dc.HAServiceView', {
|
||||||
|
title: gettext('HA Service Status'),
|
||||||
|
region: 'south',
|
||||||
|
border: false,
|
||||||
|
split: true,
|
||||||
|
flex: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
var nodegrid = Ext.create('PVE.dc.NodeView', {
|
||||||
|
title: gettext('Nodes'),
|
||||||
|
border: false,
|
||||||
|
region: 'center',
|
||||||
|
flex: 3
|
||||||
|
});
|
||||||
|
|
||||||
|
Ext.apply(me, {
|
||||||
|
layout: 'border',
|
||||||
|
items: [ nodegrid, hagrid ],
|
||||||
|
listeners: {
|
||||||
|
show: function() {
|
||||||
|
hagrid.fireEvent('show', hagrid);
|
||||||
|
nodegrid.fireEvent('show', hagrid);
|
||||||
|
},
|
||||||
|
hide: function() {
|
||||||
|
hagrid.fireEvent('hide', hagrid);
|
||||||
|
nodegrid.fireEvent('hide', hagrid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
me.callParent();
|
||||||
|
}
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user