]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Cluster.pm
node: wake-on-lan: document defaults and small style clean-up
[pve-manager.git] / PVE / API2 / Cluster.pm
CommitLineData
aff192e6
DM
1package PVE::API2::Cluster;
2
3use strict;
4use warnings;
5
a35f2aff
TL
6use JSON;
7
8use PVE::API2Tools;
9use PVE::Cluster qw(cfs_register_file cfs_lock_file cfs_read_file cfs_write_file);
3ac3653e 10use PVE::DataCenterConfig;
10cdf3ae 11use PVE::Exception qw(raise_param_exc);
a35f2aff 12use PVE::Firewall;
1f6d7f7f 13use PVE::GuestHelpers;
a35f2aff
TL
14use PVE::HA::Config;
15use PVE::HA::Env::PVE2;
a06a3eac 16use PVE::INotify;
a35f2aff
TL
17use PVE::JSONSchema qw(get_standard_option);
18use PVE::RESTHandler;
19use PVE::RPCEnvironment;
20use PVE::SafeSyslog;
aff192e6 21use PVE::Storage;
a35f2aff
TL
22use PVE::Tools qw(extract_param);
23
24use PVE::API2::ACMEAccount;
b227e9d2 25use PVE::API2::ACMEPlugin;
ac27b58d 26use PVE::API2::Backup;
df6f4b18 27use PVE::API2::Cluster::BackupInfo;
78ad23f8 28use PVE::API2::Cluster::Ceph;
797bcf9a 29use PVE::API2::Cluster::Mapping;
e190bc2c 30use PVE::API2::Cluster::Jobs;
9dedf1e2 31use PVE::API2::Cluster::MetricServer;
b6fa29f3 32use PVE::API2::Cluster::Notifications;
ac04fb55 33use PVE::API2::ClusterConfig;
4a07fced 34use PVE::API2::Firewall::Cluster;
a35f2aff 35use PVE::API2::HAConfig;
892821fd 36use PVE::API2::ReplicationConfig;
aff192e6 37
f0f63a1c
AD
38my $have_sdn;
39eval {
40 require PVE::API2::Network::SDN;
41 $have_sdn = 1;
42};
43
aff192e6
DM
44use base qw(PVE::RESTHandler);
45
892821fd
DM
46__PACKAGE__->register_method ({
47 subclass => "PVE::API2::ReplicationConfig",
48 path => 'replication',
49});
50
19e3a7f2 51__PACKAGE__->register_method ({
9dedf1e2 52 subclass => "PVE::API2::Cluster::MetricServer",
d57f8d94 53 path => 'metrics',
19e3a7f2
DC
54});
55
b6fa29f3
LW
56__PACKAGE__->register_method ({
57 subclass => "PVE::API2::Cluster::Notifications",
58 path => 'notifications',
59});
60
ac04fb55
DM
61__PACKAGE__->register_method ({
62 subclass => "PVE::API2::ClusterConfig",
63 path => 'config',
64});
65
4a07fced 66__PACKAGE__->register_method ({
cd8d0de2 67 subclass => "PVE::API2::Firewall::Cluster",
4a07fced
DM
68 path => 'firewall',
69});
70
ac27b58d 71__PACKAGE__->register_method ({
cd8d0de2 72 subclass => "PVE::API2::Backup",
ac27b58d
DM
73 path => 'backup',
74});
75
f26c7b54 76__PACKAGE__->register_method ({
df6f4b18 77 subclass => "PVE::API2::Cluster::BackupInfo",
1a2e0e23 78 path => 'backup-info',
f26c7b54
AL
79});
80
a06a3eac 81__PACKAGE__->register_method ({
cd8d0de2 82 subclass => "PVE::API2::HAConfig",
a06a3eac
DM
83 path => 'ha',
84});
85
5c3fd6ac
FG
86__PACKAGE__->register_method ({
87 subclass => "PVE::API2::ACMEAccount",
88 path => 'acme',
89});
90
78ad23f8
TL
91__PACKAGE__->register_method ({
92 subclass => "PVE::API2::Cluster::Ceph",
93 path => 'ceph',
94});
95
e190bc2c
DC
96__PACKAGE__->register_method ({
97 subclass => "PVE::API2::Cluster::Jobs",
98 path => 'jobs',
99});
797bcf9a
DC
100
101__PACKAGE__->register_method ({
102 subclass => "PVE::API2::Cluster::Mapping",
103 path => 'mapping',
104});
105
f0f63a1c
AD
106if ($have_sdn) {
107 __PACKAGE__->register_method ({
108 subclass => "PVE::API2::Network::SDN",
109 path => 'sdn',
110 });
111}
112
3ac3653e 113my $dc_schema = PVE::DataCenterConfig::get_datacenter_schema();
cd8d0de2 114my $dc_properties = {
aff192e6
DM
115 delete => {
116 type => 'string', format => 'pve-configid-list',
117 description => "A list of settings you want to delete.",
118 optional => 1,
119 }
120};
121foreach my $opt (keys %{$dc_schema->{properties}}) {
122 $dc_properties->{$opt} = $dc_schema->{properties}->{$opt};
123}
124
125__PACKAGE__->register_method ({
cd8d0de2
TM
126 name => 'index',
127 path => '',
aff192e6
DM
128 method => 'GET',
129 description => "Cluster index.",
130 permissions => { user => 'all' },
131 parameters => {
132 additionalProperties => 0,
133 properties => {},
134 },
135 returns => {
136 type => 'array',
137 items => {
138 type => "object",
139 properties => {},
140 },
141 links => [ { rel => 'child', href => "{name}" } ],
142 },
143 code => sub {
144 my ($param) = @_;
cd8d0de2 145
aff192e6 146 my $result = [
7cced712 147 { name => 'acme' },
ac27b58d 148 { name => 'backup' },
1a2e0e23 149 { name => 'backup-info' },
6e251e55 150 { name => 'ceph' },
7cced712
TL
151 { name => 'config' },
152 { name => 'firewall' },
153 { name => 'ha' },
c2069949 154 { name => 'jobs' },
7cced712 155 { name => 'log' },
797bcf9a 156 { name => 'mapping' },
d57f8d94 157 { name => 'metrics' },
b6fa29f3 158 { name => 'notifications' },
7cced712
TL
159 { name => 'nextid' },
160 { name => 'options' },
161 { name => 'replication' },
162 { name => 'resources' },
163 { name => 'status' },
164 { name => 'tasks' },
d57f8d94 165 ];
aff192e6 166
f0f63a1c
AD
167 if ($have_sdn) {
168 push(@{$result}, { name => 'sdn' });
169 }
170
aff192e6
DM
171 return $result;
172 }});
173
174__PACKAGE__->register_method({
cd8d0de2
TM
175 name => 'log',
176 path => 'log',
aff192e6
DM
177 method => 'GET',
178 description => "Read cluster log",
179 permissions => { user => 'all' },
180 parameters => {
181 additionalProperties => 0,
182 properties => {
183 max => {
184 type => 'integer',
185 description => "Maximum number of entries.",
186 optional => 1,
187 minimum => 1,
188 }
189 },
190 },
191 returns => {
192 type => 'array',
193 items => {
194 type => "object",
195 properties => {},
196 },
197 },
198 code => sub {
199 my ($param) = @_;
200
201 my $rpcenv = PVE::RPCEnvironment::get();
202
203 my $max = $param->{max} || 0;
204 my $user = $rpcenv->get_user();
205
e4d554ba 206 my $admin = $rpcenv->check($user, "/", [ 'Sys.Syslog' ], 1);
aff192e6
DM
207
208 my $loguser = $admin ? '' : $user;
209
210 my $res = decode_json(PVE::Cluster::get_cluster_log($loguser, $max));
211
0c8d7402
DC
212 foreach my $entry (@{$res->{data}}) {
213 $entry->{id} = "$entry->{uid}:$entry->{node}";
214 }
215
aff192e6
DM
216 return $res->{data};
217 }});
218
219__PACKAGE__->register_method({
cd8d0de2
TM
220 name => 'resources',
221 path => 'resources',
aff192e6
DM
222 method => 'GET',
223 description => "Resources index (cluster wide).",
224 permissions => { user => 'all' },
225 parameters => {
226 additionalProperties => 0,
badcb8d1
DM
227 properties => {
228 type => {
229 type => 'string',
230 optional => 1,
afc237df 231 enum => ['vm', 'storage', 'node', 'sdn'],
badcb8d1
DM
232 },
233 },
aff192e6
DM
234 },
235 returns => {
236 type => 'array',
237 items => {
238 type => "object",
239 properties => {
3bd90e52
WB
240 id => {
241 description => "Resource id.",
242 type => 'string',
243 },
fc6c0fdd 244 type => {
b66c604e 245 description => "Resource type.",
fc6c0fdd 246 type => 'string',
afc237df 247 enum => ['node', 'storage', 'pool', 'qemu', 'lxc', 'openvz', 'sdn'],
fc6c0fdd
DM
248 },
249 status => {
250 description => "Resource type dependent status.",
251 type => 'string',
252 optional => 1,
253 },
c5f7b4b3
HL
254 name => {
255 description => "Name of the resource.",
256 type => 'string',
257 optional => 1,
258 },
fc6c0fdd
DM
259 node => get_standard_option('pve-node', {
260 description => "The cluster node name (when type in node,storage,qemu,lxc).",
261 optional => 1,
262 }),
263 storage => get_standard_option('pve-storage-id', {
264 description => "The storage identifier (when type == storage).",
265 optional => 1,
266 }),
267 pool => {
268 description => "The pool name (when type in pool,qemu,lxc).",
269 type => 'string',
270 optional => 1,
271 },
272 cpu => {
273 description => "CPU utilization (when type in node,qemu,lxc).",
274 type => 'number',
275 optional => 1,
55fb2ead 276 minimum => 0,
fc6c0fdd
DM
277 renderer => 'fraction_as_percentage',
278 },
279 maxcpu => {
280 description => "Number of available CPUs (when type in node,qemu,lxc).",
47f86553 281 type => 'number',
fc6c0fdd 282 optional => 1,
55fb2ead 283 minimum => 0,
fc6c0fdd
DM
284 },
285 mem => {
286 description => "Used memory in bytes (when type in node,qemu,lxc).",
55fb2ead 287 type => 'integer',
fc6c0fdd
DM
288 optional => 1,
289 renderer => 'bytes',
55fb2ead 290 minimum => 0,
fc6c0fdd
DM
291 },
292 maxmem => {
293 description => "Number of available memory in bytes (when type in node,qemu,lxc).",
294 type => 'integer',
295 optional => 1,
296 renderer => 'bytes',
297 },
298 level => {
299 description => "Support level (when type == node).",
300 type => 'string',
301 optional => 1,
302 },
303 uptime => {
304 description => "Node uptime in seconds (when type in node,qemu,lxc).",
305 type => 'integer',
306 optional => 1,
307 renderer => 'duration',
308 },
309 hastate => {
310 description => "HA service status (for HA managed VMs).",
311 type => 'string',
312 optional => 1,
313 },
314 disk => {
315 description => "Used disk space in bytes (when type in storage), used root image spave for VMs (type in qemu,lxc).",
55fb2ead 316 type => 'integer',
fc6c0fdd
DM
317 optional => 1,
318 renderer => 'bytes',
55fb2ead 319 minimum => 0,
fc6c0fdd
DM
320 },
321 maxdisk => {
322 description => "Storage size in bytes (when type in storage), root image size for VMs (type in qemu,lxc).",
323 type => 'integer',
324 optional => 1,
325 renderer => 'bytes',
55fb2ead 326 minimum => 0,
fc6c0fdd 327 },
74c8984d
FE
328 content => {
329 description => "Allowed storage content types (when type == storage).",
330 type => 'string',
331 format => 'pve-storage-content-list',
332 optional => 1,
333 },
77a9ce32
TL
334 plugintype => {
335 description => "More specific type, if available.",
336 type => 'string',
337 optional => 1,
338 },
d4b49000 339 vmid => get_standard_option('pve-vmid', {
55fb2ead 340 description => "The numerical vmid (when type in qemu,lxc).",
55fb2ead 341 optional => 1,
d4b49000 342 }),
7060a393
FE
343 'cgroup-mode' => {
344 description => "The cgroup mode the node operates under (when type == node).",
345 type => 'integer',
346 optional => 1,
347 },
aff192e6
DM
348 },
349 },
350 },
351 code => sub {
352 my ($param) = @_;
353
354 my $rpcenv = PVE::RPCEnvironment::get();
84916eb2
DM
355 my $authuser = $rpcenv->get_user();
356 my $usercfg = $rpcenv->{user_cfg};
aff192e6
DM
357
358 my $res = [];
359
bc7bff8e
DM
360 my $nodelist = PVE::Cluster::get_nodelist();
361 my $members = PVE::Cluster::get_members();
aff192e6
DM
362
363 my $rrd = PVE::Cluster::rrd_dump();
364
365 my $vmlist = PVE::Cluster::get_vmlist() || {};
366 my $idlist = $vmlist->{ids} || {};
367
b67dc872 368 my $hastatus = PVE::HA::Config::read_manager_status();
8ad1127a 369 my $haresources = PVE::HA::Config::read_resources_config();
c6e94f42
DC
370 my $hatypemap = {
371 'qemu' => 'vm',
372 'lxc' => 'ct'
373 };
374
84916eb2
DM
375 my $pooldata = {};
376 if (!$param->{type} || $param->{type} eq 'pool') {
ab8ed6f4 377 for my $pool (sort keys %{$usercfg->{pools}}) {
84916eb2
DM
378 my $d = $usercfg->{pools}->{$pool};
379
91db3ece 380 next if !$rpcenv->check($authuser, "/pool/$pool", [ 'Pool.Audit' ], 1);
84916eb2
DM
381
382 my $entry = {
383 id => "/pool/$pool",
cd8d0de2 384 pool => $pool,
84916eb2
DM
385 type => 'pool',
386 };
387
388 $pooldata->{$pool} = $entry;
389
390 push @$res, $entry;
391 }
392 }
aff192e6
DM
393
394 # we try to generate 'numbers' by using "$X + 0"
badcb8d1 395 if (!$param->{type} || $param->{type} eq 'vm') {
d18b6982
DC
396 my $prop_list = [qw(lock tags)];
397 my $props = PVE::Cluster::get_guest_config_properties($prop_list);
f79372c0 398
ab8ed6f4 399 for my $vmid (sort keys %$idlist) {
aff192e6 400
19a6b9f1
DM
401 my $data = $idlist->{$vmid};
402 my $entry = PVE::API2Tools::extract_vm_stats($vmid, $data, $rrd);
f79372c0 403
c608873a
DM
404 if (my $pool = $usercfg->{vms}->{$vmid}) {
405 $entry->{pool} = $pool;
406 if (my $pe = $pooldata->{$pool}) {
407 if ($entry->{uptime}) {
84916eb2
DM
408 $pe->{uptime} = $entry->{uptime} if !$pe->{uptime} || $entry->{uptime} > $pe->{uptime};
409 $pe->{mem} = 0 if !$pe->{mem};
410 $pe->{mem} += $entry->{mem};
411 $pe->{maxmem} = 0 if !$pe->{maxmem};
412 $pe->{maxmem} += $entry->{maxmem};
413 $pe->{cpu} = 0 if !$pe->{cpu};
c37f23f5 414 $pe->{maxcpu} = 0 if !$pe->{maxcpu};
ffe31eea
DC
415 # explanation:
416 # we do not know how much cpus there are in the cluster at this moment
417 # so we calculate the current % of the cpu
418 # but we had already the old cpu % before this vm, so:
419 # new% = (old%*oldmax + cur%*curmax) / (oldmax+curmax)
ffe31eea 420 $pe->{cpu} = (($pe->{cpu} * $pe->{maxcpu}) + ($entry->{cpu} * $entry->{maxcpu})) / ($pe->{maxcpu} + $entry->{maxcpu});
84916eb2
DM
421 $pe->{maxcpu} += $entry->{maxcpu};
422 }
423 }
bc7bff8e 424 }
cd8d0de2 425
424c94fe 426 # only skip now to next to ensure that the pool stats above are filled, if eligible
a285f014
DM
427 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
428
d18b6982
DC
429 for my $prop (@$prop_list) {
430 if (defined(my $value = $props->{$vmid}->{$prop})) {
431 $entry->{$prop} = $value;
432 }
424c94fe
TL
433 }
434
91db3ece
LS
435 if (defined($entry->{pool}) &&
436 !$rpcenv->check($authuser, "/pool/$entry->{pool}", ['Pool.Audit'], 1)) {
437 delete $entry->{pool};
438 }
439
c6e94f42 440 # get ha status
b67dc872
DM
441 if (my $hatype = $hatypemap->{$entry->{type}}) {
442 my $sid = "$hatype:$vmid";
8ad1127a
TL
443 my $service;
444 if ($service = $hastatus->{service_status}->{$sid}) {
445 $entry->{hastate} = $service->{state};
446 } elsif ($service = $haresources->{ids}->{$sid}) {
447 $entry->{hastate} = $service->{state};
b67dc872 448 }
c6e94f42
DC
449 }
450
badcb8d1 451 push @$res, $entry;
aff192e6 452 }
aff192e6
DM
453 }
454
7060a393
FE
455 my $static_node_info = PVE::Cluster::get_node_kv("static-info");
456
badcb8d1 457 if (!$param->{type} || $param->{type} eq 'node') {
bc7bff8e 458 foreach my $node (@$nodelist) {
57d56896
TL
459 my $can_audit = $rpcenv->check($authuser, "/nodes/$node", [ 'Sys.Audit' ], 1);
460 my $entry = PVE::API2Tools::extract_node_stats($node, $members, $rrd, !$can_audit);
7060a393
FE
461
462 my $info = eval { decode_json($static_node_info->{$node}); };
463 if (defined(my $mode = $info->{'cgroup-mode'})) {
464 $entry->{'cgroup-mode'} = int($mode);
465 }
466
aff192e6 467 push @$res, $entry;
badcb8d1
DM
468 }
469 }
aff192e6 470
badcb8d1
DM
471 if (!$param->{type} || $param->{type} eq 'storage') {
472
473 my $cfg = PVE::Storage::config();
474 my @sids = PVE::Storage::storage_ids ($cfg);
475
476 foreach my $storeid (@sids) {
84916eb2 477 next if !$rpcenv->check($authuser, "/storage/$storeid", [ 'Datastore.Audit' ], 1);
19a6b9f1
DM
478
479 my $scfg = PVE::Storage::storage_config($cfg, $storeid);
badcb8d1
DM
480 # we create a entry for each node
481 foreach my $node (@$nodelist) {
482 next if !PVE::Storage::storage_check_enabled($cfg, $storeid, $node, 1);
badcb8d1 483
19a6b9f1 484 my $entry = PVE::API2Tools::extract_storage_stats($storeid, $scfg, $node, $rrd);
badcb8d1
DM
485 push @$res, $entry;
486 }
aff192e6
DM
487 }
488 }
489
9ed5d4f5
FG
490 if (!$param->{type} || $param->{type} eq 'sdn') {
491 #add default "localnetwork" zone
492 if ($rpcenv->check($authuser, "/sdn/zones/localnetwork", [ 'SDN.Audit' ], 1)) {
493 foreach my $node (@$nodelist) {
494 my $local_sdn = {
495 id => "sdn/$node/localnetwork",
496 sdn => 'localnetwork',
497 node => $node,
498 type => 'sdn',
499 status => 'ok',
500 };
501 push @$res, $local_sdn;
502 }
cdc140f0 503 }
afc237df 504
9ed5d4f5 505 if ($have_sdn) {
afc237df
AD
506 my $nodes = PVE::Cluster::get_node_kv("sdn");
507
ab8ed6f4 508 for my $node (sort keys %{$nodes}) {
afc237df
AD
509 my $sdns = decode_json($nodes->{$node});
510
ab8ed6f4 511 for my $id (sort keys %{$sdns}) {
9afcbd26 512 next if !$rpcenv->check($authuser, "/sdn/zones/$id", [ 'SDN.Audit' ], 1);
ab8ed6f4 513 my $sdn = $sdns->{$id};
afc237df
AD
514 my $entry = {
515 id => "sdn/$node/$id",
516 sdn => $id,
517 node => $node,
518 type => 'sdn',
519 status => $sdn->{'status'},
eb5cc908
TL
520 };
521 push @$res, $entry;
afc237df 522 }
9ed5d4f5 523 }
afc237df
AD
524 }
525 }
526
aff192e6
DM
527 return $res;
528 }});
529
530__PACKAGE__->register_method({
cd8d0de2
TM
531 name => 'tasks',
532 path => 'tasks',
aff192e6
DM
533 method => 'GET',
534 description => "List recent tasks (cluster wide).",
535 permissions => { user => 'all' },
536 parameters => {
537 additionalProperties => 0,
538 properties => {},
539 },
540 returns => {
541 type => 'array',
542 items => {
543 type => "object",
544 properties => {
545 upid => { type => 'string' },
546 },
547 },
548 },
549 code => sub {
550 my ($param) = @_;
551
552 my $rpcenv = PVE::RPCEnvironment::get();
84916eb2 553 my $authuser = $rpcenv->get_user();
aff192e6
DM
554
555 my $tlist = PVE::Cluster::get_tasklist();
7658f010 556 return [] if !$tlist;
aff192e6 557
84916eb2 558 my $all = $rpcenv->check($authuser, "/", [ 'Sys.Audit' ], 1);
aff192e6 559
7658f010 560 my $res = [];
aff192e6 561 foreach my $task (@$tlist) {
a901f94a
FG
562 if (PVE::AccessControl::pve_verify_tokenid($task->{user}, 1)) {
563 ($task->{user}, $task->{tokenid}) = PVE::AccessControl::split_tokenid($task->{user});
564 }
84916eb2 565 push @$res, $task if $all || ($task->{user} eq $authuser);
aff192e6 566 }
cd8d0de2 567
aff192e6
DM
568 return $res;
569 }});
570
571__PACKAGE__->register_method({
cd8d0de2
TM
572 name => 'get_options',
573 path => 'options',
aff192e6 574 method => 'GET',
1f6d7f7f 575 description => "Get datacenter options. Without 'Sys.Audit' on '/' not all options are returned.",
aff192e6 576 permissions => {
1f6d7f7f 577 user => 'all',
7d020b42 578 check => ['perm', '/', [ 'Sys.Audit' ]],
aff192e6
DM
579 },
580 parameters => {
581 additionalProperties => 0,
582 properties => {},
583 },
584 returns => {
585 type => "object",
586 properties => {},
587 },
588 code => sub {
589 my ($param) = @_;
449f1b5d 590
1f6d7f7f
DC
591 my $res = {};
592
593 my $rpcenv = PVE::RPCEnvironment::get();
594 my $authuser = $rpcenv->get_user();
595
596 my $datacenter_config = eval { PVE::Cluster::cfs_read_file('datacenter.cfg') } // {};
597
598 if ($rpcenv->check($authuser, '/', ['Sys.Audit'], 1)) {
599 $res = $datacenter_config;
600 } else {
601 for my $k (qw(console tag-style)) {
602 $res->{$k} = $datacenter_config->{$k} if exists $datacenter_config->{$k};
603 }
604 }
605
606 my $tags = PVE::GuestHelpers::get_allowed_tags($rpcenv, $authuser);
607 $res->{'allowed-tags'} = [sort keys $tags->%*];
608
609 return $res;
aff192e6
DM
610 }});
611
612__PACKAGE__->register_method({
cd8d0de2
TM
613 name => 'set_options',
614 path => 'options',
aff192e6
DM
615 method => 'PUT',
616 description => "Set datacenter options.",
617 permissions => {
7d020b42 618 check => ['perm', '/', [ 'Sys.Modify' ]],
aff192e6
DM
619 },
620 protected => 1,
621 parameters => {
622 additionalProperties => 0,
623 properties => $dc_properties,
624 },
625 returns => { type => "null" },
626 code => sub {
627 my ($param) = @_;
628
aff192e6
DM
629 my $delete = extract_param($param, 'delete');
630
42f82359
TL
631 cfs_lock_file('datacenter.cfg', undef, sub {
632 my $conf = cfs_read_file('datacenter.cfg');
aff192e6 633
42f82359 634 $conf->{$_} = $param->{$_} for keys $param->%*;
aff192e6 635
42f82359 636 delete $conf->{$_} for PVE::Tools::split_list($delete);
aff192e6 637
42f82359
TL
638 cfs_write_file('datacenter.cfg', $conf);
639 });
aff192e6
DM
640 die $@ if $@;
641
642 return undef;
643 }});
644
a0af0132 645__PACKAGE__->register_method({
cd8d0de2
TM
646 name => 'get_status',
647 path => 'status',
a0af0132 648 method => 'GET',
76189130 649 description => "Get cluster status information.",
449f1b5d
DM
650 permissions => {
651 check => ['perm', '/', [ 'Sys.Audit' ]],
652 },
a0af0132
DM
653 protected => 1,
654 parameters => {
655 additionalProperties => 0,
656 properties => {},
657 },
658 returns => {
659 type => 'array',
660 items => {
661 type => "object",
662 properties => {
663 type => {
e9b2e291
TM
664 type => 'string',
665 enum => ['cluster', 'node'],
666 description => 'Indicates the type, either cluster or node. The type defines the object properties e.g. quorate available for type cluster.'
667 },
668 id => {
669 type => 'string',
670 },
671 name => {
672 type => 'string',
673 },
674 nodes => {
675 type => 'integer',
676 optional => 1,
677 description => '[cluster] Nodes count, including offline nodes.',
678 },
679 version => {
680 type => 'integer',
681 optional => 1,
682 description => '[cluster] Current version of the corosync configuration file.',
a0af0132 683 },
e9b2e291
TM
684 quorate => {
685 type => 'boolean',
686 optional => 1,
687 description => '[cluster] Indicates if there is a majority of nodes online to make decisions',
688 },
689 nodeid => {
690 type => 'integer',
691 optional => 1,
692 description => '[node] ID of the node from the corosync configuration.',
693 },
694 ip => {
695 type => 'string',
696 optional => 1,
06855f12 697 description => '[node] IP of the resolved nodename.',
e9b2e291
TM
698 },
699 'local' => {
700 type => 'boolean',
701 optional => 1,
702 description => '[node] Indicates if this is the responding node.',
703 },
704 online => {
705 type => 'boolean',
706 optional => 1,
707 description => '[node] Indicates if the node is online or offline.',
708 },
709 level => {
710 type => 'string',
711 optional => 1,
712 description => '[node] Proxmox VE Subscription level, indicates if eligible for enterprise support as well as access to the stable Proxmox VE Enterprise Repository.',
713 }
a0af0132
DM
714 },
715 },
716 },
717 code => sub {
718 my ($param) = @_;
719
e1c20e2a
FG
720 # make sure we get current info
721 PVE::Cluster::cfs_update();
722
a0af0132 723 # we also add info from pmxcfs
cd8d0de2 724 my $clinfo = PVE::Cluster::get_clinfo();
a0af0132
DM
725 my $members = PVE::Cluster::get_members();
726 my $nodename = PVE::INotify::nodename();
16b69b6c 727 my $rrd = PVE::Cluster::rrd_dump();
a0af0132
DM
728
729 if ($members) {
91d7c7aa
DM
730 my $res = [];
731
732 if (my $d = $clinfo->{cluster}) {
733 push @$res, {
734 type => 'cluster',
735 id => 'cluster',
736 nodes => $d->{nodes},
737 version => $d->{version},
738 name => $d->{name},
739 quorate => $d->{quorate},
740 };
741 }
cd8d0de2 742
91d7c7aa
DM
743 foreach my $node (keys %$members) {
744 my $d = $members->{$node};
cd8d0de2 745 my $entry = {
91d7c7aa
DM
746 type => 'node',
747 id => "node/$node",
748 name => $node,
749 nodeid => $d->{id},
91d7c7aa
DM
750 'local' => ($node eq $nodename) ? 1 : 0,
751 online => $d->{online},
752 };
cd8d0de2 753
122020b1
TM
754 if (defined($d->{ip})) {
755 $entry->{ip} = $d->{ip};
756 }
757
91d7c7aa 758 if (my $d = PVE::API2Tools::extract_node_stats($node, $members, $rrd)) {
122020b1 759 $entry->{level} = $d->{level} || '';
91d7c7aa 760 }
cd8d0de2 761
91d7c7aa
DM
762 push @$res, $entry;
763 }
764 return $res;
a0af0132
DM
765 } else {
766 # fake entry for local node if no cluster defined
767 my $pmxcfs = ($clinfo && $clinfo->{version}) ? 1 : 0; # pmxcfs online ?
16b69b6c 768
d017de1f 769 my $subinfo = PVE::API2::Subscription::read_etc_subscription();
16b69b6c
DM
770 my $sublevel = $subinfo->{level} || '';
771
a0af0132
DM
772 return [{
773 type => 'node',
774 id => "node/$nodename",
775 name => $nodename,
53012285 776 ip => scalar(PVE::Cluster::remote_node_ip($nodename)),
a0af0132
DM
777 'local' => 1,
778 nodeid => 0,
91d7c7aa 779 online => 1,
16b69b6c 780 level => $sublevel,
a0af0132
DM
781 }];
782 }
783 }});
784
10cdf3ae 785__PACKAGE__->register_method({
cd8d0de2
TM
786 name => 'nextid',
787 path => 'nextid',
10cdf3ae 788 method => 'GET',
42f82359 789 description => "Get next free VMID. Pass a VMID to assert that its free (at time of check).",
10cdf3ae
DM
790 permissions => { user => 'all' },
791 parameters => {
792 additionalProperties => 0,
793 properties => {
42f82359
TL
794 vmid => get_standard_option('pve-vmid', {
795 optional => 1,
796 }),
10cdf3ae
DM
797 },
798 },
799 returns => {
800 type => 'integer',
801 description => "The next free VMID.",
802 },
803 code => sub {
804 my ($param) = @_;
805
806 my $vmlist = PVE::Cluster::get_vmlist() || {};
807 my $idlist = $vmlist->{ids} || {};
808
809 if (my $vmid = $param->{vmid}) {
810 return $vmid if !defined($idlist->{$vmid});
811 raise_param_exc({ vmid => "VM $vmid already exists" });
812 }
813
ca65e099
TL
814 my $dc_conf = PVE::Cluster::cfs_read_file('datacenter.cfg');
815 my $next_id = $dc_conf->{'next-id'} // {};
816
817 my $lower = $next_id->{lower} // 100;
818 my $upper = $next_id->{upper} // (1000 * 1000); # note, lower than the schema-maximum
819
820 for (my $i = $lower; $i < $upper; $i++) {
10cdf3ae
DM
821 return $i if !defined($idlist->{$i});
822 }
823
ca65e099 824 die "unable to get any free VMID in range [$lower, $upper]\n";
10cdf3ae
DM
825 }});
826
aff192e6 8271;