use PVE::Tools qw(extract_param);
use PVE::API2::ACMEAccount;
+use PVE::API2::ACMEPlugin;
use PVE::API2::Backup;
+use PVE::API2::Cluster::BackupInfo;
use PVE::API2::Cluster::Ceph;
+use PVE::API2::Cluster::Jobs;
+use PVE::API2::Cluster::MetricServer;
use PVE::API2::ClusterConfig;
use PVE::API2::Firewall::Cluster;
use PVE::API2::HAConfig;
path => 'replication',
});
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::Cluster::MetricServer",
+ path => 'metrics',
+});
+
__PACKAGE__->register_method ({
subclass => "PVE::API2::ClusterConfig",
path => 'config',
path => 'backup',
});
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::Cluster::BackupInfo",
+ path => 'backup-info',
+});
+
__PACKAGE__->register_method ({
subclass => "PVE::API2::HAConfig",
path => 'ha',
path => 'ceph',
});
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::Cluster::Jobs",
+ path => 'jobs',
+});
if ($have_sdn) {
__PACKAGE__->register_method ({
subclass => "PVE::API2::Network::SDN",
my ($param) = @_;
my $result = [
+ { name => 'acme' },
+ { name => 'backup' },
+ { name => 'backup-info' },
+ { name => 'ceph' },
+ { name => 'config' },
+ { name => 'firewall' },
+ { name => 'ha' },
+ { name => 'jobs' },
{ name => 'log' },
+ { name => 'metrics' },
+ { name => 'nextid' },
{ name => 'options' },
- { name => 'resources' },
{ name => 'replication' },
- { name => 'tasks' },
- { name => 'backup' },
- { name => 'ha' },
+ { name => 'resources' },
{ name => 'status' },
- { name => 'nextid' },
- { name => 'firewall' },
- { name => 'config' },
- { name => 'acme' },
- { name => 'ceph' },
- ];
+ { name => 'tasks' },
+ ];
if ($have_sdn) {
push(@{$result}, { name => 'sdn' });
type => 'string',
optional => 1,
},
+ name => {
+ description => "Name of the resource.",
+ type => 'string',
+ optional => 1,
+ },
node => get_standard_option('pve-node', {
description => "The cluster node name (when type in node,storage,qemu,lxc).",
optional => 1,
description => "CPU utilization (when type in node,qemu,lxc).",
type => 'number',
optional => 1,
+ minimum => 0,
renderer => 'fraction_as_percentage',
},
maxcpu => {
description => "Number of available CPUs (when type in node,qemu,lxc).",
type => 'number',
optional => 1,
+ minimum => 0,
},
mem => {
description => "Used memory in bytes (when type in node,qemu,lxc).",
- type => 'string',
+ type => 'integer',
optional => 1,
renderer => 'bytes',
+ minimum => 0,
},
maxmem => {
description => "Number of available memory in bytes (when type in node,qemu,lxc).",
},
disk => {
description => "Used disk space in bytes (when type in storage), used root image spave for VMs (type in qemu,lxc).",
- type => 'string',
+ type => 'integer',
optional => 1,
renderer => 'bytes',
+ minimum => 0,
},
maxdisk => {
description => "Storage size in bytes (when type in storage), root image size for VMs (type in qemu,lxc).",
type => 'integer',
optional => 1,
renderer => 'bytes',
+ minimum => 0,
+ },
+ content => {
+ description => "Allowed storage content types (when type == storage).",
+ type => 'string',
+ format => 'pve-storage-content-list',
+ optional => 1,
+ },
+ plugintype => {
+ description => "More specific type, if available.",
+ type => 'string',
+ optional => 1,
+ },
+ vmid => {
+ description => "The numerical vmid (when type in qemu,lxc).",
+ type => 'integer',
+ optional => 1,
+ minimum => 1,
},
},
},
my $pooldata = {};
if (!$param->{type} || $param->{type} eq 'pool') {
- foreach my $pool (keys %{$usercfg->{pools}}) {
+ for my $pool (sort keys %{$usercfg->{pools}}) {
my $d = $usercfg->{pools}->{$pool};
- next if !$rpcenv->check($authuser, "/pool/$pool", [ 'Pool.Allocate' ], 1);
+ next if !$rpcenv->check($authuser, "/pool/$pool", [ 'Pool.Audit' ], 1);
my $entry = {
id => "/pool/$pool",
if (!$param->{type} || $param->{type} eq 'vm') {
my $locked_vms = PVE::Cluster::get_guest_config_property('lock');
- foreach my $vmid (keys %$idlist) {
+ for my $vmid (sort keys %$idlist) {
my $data = $idlist->{$vmid};
my $entry = PVE::API2Tools::extract_vm_stats($vmid, $data, $rrd);
- if (defined(my $lock = $locked_vms->{$vmid}->{lock})) {
- $entry->{lock} = $lock;
- }
-
if (my $pool = $usercfg->{vms}->{$vmid}) {
$entry->{pool} = $pool;
if (my $pe = $pooldata->{$pool}) {
}
}
+ # only skip now to next to ensure that the pool stats above are filled, if eligible
next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
+ if (defined(my $lock = $locked_vms->{$vmid}->{lock})) {
+ $entry->{lock} = $lock;
+ }
+
+ if (defined($entry->{pool}) &&
+ !$rpcenv->check($authuser, "/pool/$entry->{pool}", ['Pool.Audit'], 1)) {
+ delete $entry->{pool};
+ }
+
# get ha status
if (my $hatype = $hatypemap->{$entry->{type}}) {
my $sid = "$hatype:$vmid";
my $nodes = PVE::Cluster::get_node_kv("sdn");
- foreach my $node (keys %{$nodes}) {
+ for my $node (sort keys %{$nodes}) {
my $sdns = decode_json($nodes->{$node});
- foreach my $id (keys %{$sdns}) {
+ for my $id (sort keys %{$sdns}) {
+ next if !$rpcenv->check($authuser, "/sdn/zones/$id", [ 'SDN.Audit' ], 1);
my $sdn = $sdns->{$id};
- #next if !$rpcenv->check($authuser, "/sdn/$id", [ 'SDN.Audit' ], 1);
my $entry = {
id => "sdn/$node/$id",
sdn => $id,
my $authuser = $rpcenv->get_user();
my $tlist = PVE::Cluster::get_tasklist();
-
- my $res = [];
-
- return $res if !$tlist;
+ return [] if !$tlist;
my $all = $rpcenv->check($authuser, "/", [ 'Sys.Audit' ], 1);
+ my $res = [];
foreach my $task (@$tlist) {
+ if (PVE::AccessControl::pve_verify_tokenid($task->{user}, 1)) {
+ ($task->{user}, $task->{tokenid}) = PVE::AccessControl::split_tokenid($task->{user});
+ }
push @$res, $task if $all || ($task->{user} eq $authuser);
}
code => sub {
my ($param) = @_;
- my $filename = 'datacenter.cfg';
-
my $delete = extract_param($param, 'delete');
- my $code = sub {
-
- my $conf = cfs_read_file($filename);
+ cfs_lock_file('datacenter.cfg', undef, sub {
+ my $conf = cfs_read_file('datacenter.cfg');
- foreach my $opt (keys %$param) {
- $conf->{$opt} = $param->{$opt};
- }
-
- foreach my $opt (PVE::Tools::split_list($delete)) {
- delete $conf->{$opt};
- };
+ $conf->{$_} = $param->{$_} for keys $param->%*;
- cfs_write_file($filename, $conf);
- };
+ delete $conf->{$_} for PVE::Tools::split_list($delete);
- cfs_lock_file($filename, undef, $code);
+ cfs_write_file('datacenter.cfg', $conf);
+ });
die $@ if $@;
return undef;
type => "object",
properties => {
type => {
- type => 'string'
+ type => 'string',
+ enum => ['cluster', 'node'],
+ description => 'Indicates the type, either cluster or node. The type defines the object properties e.g. quorate available for type cluster.'
+ },
+ id => {
+ type => 'string',
},
+ name => {
+ type => 'string',
+ },
+ nodes => {
+ type => 'integer',
+ optional => 1,
+ description => '[cluster] Nodes count, including offline nodes.',
+ },
+ version => {
+ type => 'integer',
+ optional => 1,
+ description => '[cluster] Current version of the corosync configuration file.',
+ },
+ quorate => {
+ type => 'boolean',
+ optional => 1,
+ description => '[cluster] Indicates if there is a majority of nodes online to make decisions',
+ },
+ nodeid => {
+ type => 'integer',
+ optional => 1,
+ description => '[node] ID of the node from the corosync configuration.',
+ },
+ ip => {
+ type => 'string',
+ optional => 1,
+ description => '[node] IP of the resolved nodename.',
+ },
+ 'local' => {
+ type => 'boolean',
+ optional => 1,
+ description => '[node] Indicates if this is the responding node.',
+ },
+ online => {
+ type => 'boolean',
+ optional => 1,
+ description => '[node] Indicates if the node is online or offline.',
+ },
+ level => {
+ type => 'string',
+ optional => 1,
+ description => '[node] Proxmox VE Subscription level, indicates if eligible for enterprise support as well as access to the stable Proxmox VE Enterprise Repository.',
+ }
},
},
},
id => "node/$node",
name => $node,
nodeid => $d->{id},
- ip => $d->{ip},
'local' => ($node eq $nodename) ? 1 : 0,
online => $d->{online},
};
+ if (defined($d->{ip})) {
+ $entry->{ip} = $d->{ip};
+ }
+
if (my $d = PVE::API2Tools::extract_node_stats($node, $members, $rrd)) {
- $entry->{level} = $d->{level};
+ $entry->{level} = $d->{level} || '';
}
push @$res, $entry;
# fake entry for local node if no cluster defined
my $pmxcfs = ($clinfo && $clinfo->{version}) ? 1 : 0; # pmxcfs online ?
- my $subinfo = PVE::INotify::read_file('subscription');
+ my $subinfo = PVE::API2::Subscription::read_etc_subscription();
my $sublevel = $subinfo->{level} || '';
return [{
name => 'nextid',
path => 'nextid',
method => 'GET',
- description => "Get next free VMID. If you pass an VMID it will raise an error if the ID is already used.",
+ description => "Get next free VMID. Pass a VMID to assert that its free (at time of check).",
permissions => { user => 'all' },
parameters => {
additionalProperties => 0,
properties => {
- vmid => get_standard_option('pve-vmid', {optional => 1}),
+ vmid => get_standard_option('pve-vmid', {
+ optional => 1,
+ }),
},
},
returns => {
raise_param_exc({ vmid => "VM $vmid already exists" });
}
- for (my $i = 100; $i < 10000; $i++) {
+ my $dc_conf = PVE::Cluster::cfs_read_file('datacenter.cfg');
+ my $next_id = $dc_conf->{'next-id'} // {};
+
+ my $lower = $next_id->{lower} // 100;
+ my $upper = $next_id->{upper} // (1000 * 1000); # note, lower than the schema-maximum
+
+ for (my $i = $lower; $i < $upper; $i++) {
return $i if !defined($idlist->{$i});
}
- die "unable to get any free VMID\n";
+ die "unable to get any free VMID in range [$lower, $upper]\n";
}});
1;