1 package PVE
::API2
::Cluster
;
9 use PVE
::Cluster
qw(cfs_register_file cfs_lock_file cfs_read_file cfs_write_file);
10 use PVE
::DataCenterConfig
;
11 use PVE
::Exception
qw(raise_param_exc);
14 use PVE
::HA
::Env
::PVE2
;
16 use PVE
::JSONSchema
qw(get_standard_option);
18 use PVE
::RPCEnvironment
;
21 use PVE
::Tools
qw(extract_param);
23 use PVE
::API2
::ACMEAccount
;
24 use PVE
::API2
::Backup
;
25 use PVE
::API2
::Cluster
::Ceph
;
26 use PVE
::API2
::ClusterConfig
;
27 use PVE
::API2
::Firewall
::Cluster
;
28 use PVE
::API2
::HAConfig
;
29 use PVE
::API2
::ReplicationConfig
;
33 require PVE
::API2
::Network
::SDN
;
37 use base
qw(PVE::RESTHandler);
39 __PACKAGE__-
>register_method ({
40 subclass
=> "PVE::API2::ReplicationConfig",
41 path
=> 'replication',
44 __PACKAGE__-
>register_method ({
45 subclass
=> "PVE::API2::ClusterConfig",
49 __PACKAGE__-
>register_method ({
50 subclass
=> "PVE::API2::Firewall::Cluster",
54 __PACKAGE__-
>register_method ({
55 subclass
=> "PVE::API2::Backup",
59 __PACKAGE__-
>register_method ({
60 subclass
=> "PVE::API2::HAConfig",
64 __PACKAGE__-
>register_method ({
65 subclass
=> "PVE::API2::ACMEAccount",
69 __PACKAGE__-
>register_method ({
70 subclass
=> "PVE::API2::Cluster::Ceph",
75 __PACKAGE__-
>register_method ({
76 subclass
=> "PVE::API2::Network::SDN",
81 my $dc_schema = PVE
::DataCenterConfig
::get_datacenter_schema
();
84 type
=> 'string', format
=> 'pve-configid-list',
85 description
=> "A list of settings you want to delete.",
89 foreach my $opt (keys %{$dc_schema->{properties
}}) {
90 $dc_properties->{$opt} = $dc_schema->{properties
}->{$opt};
93 __PACKAGE__-
>register_method ({
97 description
=> "Cluster index.",
98 permissions
=> { user
=> 'all' },
100 additionalProperties
=> 0,
109 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
116 { name
=> 'options' },
117 { name
=> 'resources' },
118 { name
=> 'replication' },
120 { name
=> 'backup' },
122 { name
=> 'status' },
123 { name
=> 'nextid' },
124 { name
=> 'firewall' },
125 { name
=> 'config' },
131 push(@{$result}, { name
=> 'sdn' });
137 __PACKAGE__-
>register_method({
141 description
=> "Read cluster log",
142 permissions
=> { user
=> 'all' },
144 additionalProperties
=> 0,
148 description
=> "Maximum number of entries.",
164 my $rpcenv = PVE
::RPCEnvironment
::get
();
166 my $max = $param->{max
} || 0;
167 my $user = $rpcenv->get_user();
169 my $admin = $rpcenv->check($user, "/", [ 'Sys.Syslog' ], 1);
171 my $loguser = $admin ?
'' : $user;
173 my $res = decode_json
(PVE
::Cluster
::get_cluster_log
($loguser, $max));
175 foreach my $entry (@{$res->{data
}}) {
176 $entry->{id
} = "$entry->{uid}:$entry->{node}";
182 __PACKAGE__-
>register_method({
186 description
=> "Resources index (cluster wide).",
187 permissions
=> { user
=> 'all' },
189 additionalProperties
=> 0,
194 enum
=> ['vm', 'storage', 'node', 'sdn'],
203 id
=> { type
=> 'string' },
205 description
=> "Resource type.",
207 enum
=> ['node', 'storage', 'pool', 'qemu', 'lxc', 'openvz', 'sdn'],
210 description
=> "Resource type dependent status.",
214 node
=> get_standard_option
('pve-node', {
215 description
=> "The cluster node name (when type in node,storage,qemu,lxc).",
218 storage
=> get_standard_option
('pve-storage-id', {
219 description
=> "The storage identifier (when type == storage).",
223 description
=> "The pool name (when type in pool,qemu,lxc).",
228 description
=> "CPU utilization (when type in node,qemu,lxc).",
231 renderer
=> 'fraction_as_percentage',
234 description
=> "Number of available CPUs (when type in node,qemu,lxc).",
239 description
=> "Used memory in bytes (when type in node,qemu,lxc).",
245 description
=> "Number of available memory in bytes (when type in node,qemu,lxc).",
251 description
=> "Support level (when type == node).",
256 description
=> "Node uptime in seconds (when type in node,qemu,lxc).",
259 renderer
=> 'duration',
262 description
=> "HA service status (for HA managed VMs).",
267 description
=> "Used disk space in bytes (when type in storage), used root image spave for VMs (type in qemu,lxc).",
273 description
=> "Storage size in bytes (when type in storage), root image size for VMs (type in qemu,lxc).",
284 my $rpcenv = PVE
::RPCEnvironment
::get
();
285 my $authuser = $rpcenv->get_user();
286 my $usercfg = $rpcenv->{user_cfg
};
290 my $nodelist = PVE
::Cluster
::get_nodelist
();
291 my $members = PVE
::Cluster
::get_members
();
293 my $rrd = PVE
::Cluster
::rrd_dump
();
295 my $vmlist = PVE
::Cluster
::get_vmlist
() || {};
296 my $idlist = $vmlist->{ids
} || {};
298 my $hastatus = PVE
::HA
::Config
::read_manager_status
();
299 my $haresources = PVE
::HA
::Config
::read_resources_config
();
306 if (!$param->{type
} || $param->{type
} eq 'pool') {
307 foreach my $pool (keys %{$usercfg->{pools
}}) {
308 my $d = $usercfg->{pools
}->{$pool};
310 next if !$rpcenv->check($authuser, "/pool/$pool", [ 'Pool.Allocate' ], 1);
318 $pooldata->{$pool} = $entry;
324 # we try to generate 'numbers' by using "$X + 0"
325 if (!$param->{type
} || $param->{type
} eq 'vm') {
326 my $locked_vms = PVE
::Cluster
::get_guest_config_property
('lock');
328 foreach my $vmid (keys %$idlist) {
330 my $data = $idlist->{$vmid};
331 my $entry = PVE
::API2Tools
::extract_vm_stats
($vmid, $data, $rrd);
333 if (defined(my $lock = $locked_vms->{$vmid}->{lock})) {
334 $entry->{lock} = $lock;
337 if (my $pool = $usercfg->{vms
}->{$vmid}) {
338 $entry->{pool
} = $pool;
339 if (my $pe = $pooldata->{$pool}) {
340 if ($entry->{uptime
}) {
341 $pe->{uptime
} = $entry->{uptime
} if !$pe->{uptime
} || $entry->{uptime
} > $pe->{uptime
};
342 $pe->{mem
} = 0 if !$pe->{mem
};
343 $pe->{mem
} += $entry->{mem
};
344 $pe->{maxmem
} = 0 if !$pe->{maxmem
};
345 $pe->{maxmem
} += $entry->{maxmem
};
346 $pe->{cpu
} = 0 if !$pe->{cpu
};
347 $pe->{maxcpu
} = 0 if !$pe->{maxcpu
};
349 # we do not know how much cpus there are in the cluster at this moment
350 # so we calculate the current % of the cpu
351 # but we had already the old cpu % before this vm, so:
352 # new% = (old%*oldmax + cur%*curmax) / (oldmax+curmax)
353 $pe->{cpu
} = (($pe->{cpu
} * $pe->{maxcpu
}) + ($entry->{cpu
} * $entry->{maxcpu
})) / ($pe->{maxcpu
} + $entry->{maxcpu
});
354 $pe->{maxcpu
} += $entry->{maxcpu
};
359 next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
362 if (my $hatype = $hatypemap->{$entry->{type
}}) {
363 my $sid = "$hatype:$vmid";
365 if ($service = $hastatus->{service_status
}->{$sid}) {
366 $entry->{hastate
} = $service->{state};
367 } elsif ($service = $haresources->{ids
}->{$sid}) {
368 $entry->{hastate
} = $service->{state};
376 if (!$param->{type
} || $param->{type
} eq 'node') {
377 foreach my $node (@$nodelist) {
378 my $can_audit = $rpcenv->check($authuser, "/nodes/$node", [ 'Sys.Audit' ], 1);
379 my $entry = PVE
::API2Tools
::extract_node_stats
($node, $members, $rrd, !$can_audit);
384 if (!$param->{type
} || $param->{type
} eq 'storage') {
386 my $cfg = PVE
::Storage
::config
();
387 my @sids = PVE
::Storage
::storage_ids
($cfg);
389 foreach my $storeid (@sids) {
390 next if !$rpcenv->check($authuser, "/storage/$storeid", [ 'Datastore.Audit' ], 1);
392 my $scfg = PVE
::Storage
::storage_config
($cfg, $storeid);
393 # we create a entry for each node
394 foreach my $node (@$nodelist) {
395 next if !PVE
::Storage
::storage_check_enabled
($cfg, $storeid, $node, 1);
397 my $entry = PVE
::API2Tools
::extract_storage_stats
($storeid, $scfg, $node, $rrd);
404 if (!$param->{type
} || $param->{type
} eq 'sdn') {
406 my $nodes = PVE
::Cluster
::get_node_kv
("sdn");
408 foreach my $node (keys %{$nodes}) {
409 my $sdns = decode_json
($nodes->{$node});
411 foreach my $id (keys %{$sdns}) {
412 my $sdn = $sdns->{$id};
413 #next if !$rpcenv->check($authuser, "/sdn/$id", [ 'SDN.Audit' ], 1);
415 id
=> "sdn/$node/$id",
419 status
=> $sdn->{'status'},
430 __PACKAGE__-
>register_method({
434 description
=> "List recent tasks (cluster wide).",
435 permissions
=> { user
=> 'all' },
437 additionalProperties
=> 0,
445 upid
=> { type
=> 'string' },
452 my $rpcenv = PVE
::RPCEnvironment
::get
();
453 my $authuser = $rpcenv->get_user();
455 my $tlist = PVE
::Cluster
::get_tasklist
();
459 return $res if !$tlist;
461 my $all = $rpcenv->check($authuser, "/", [ 'Sys.Audit' ], 1);
463 foreach my $task (@$tlist) {
464 push @$res, $task if $all || ($task->{user
} eq $authuser);
470 __PACKAGE__-
>register_method({
471 name
=> 'get_options',
474 description
=> "Get datacenter options.",
476 check
=> ['perm', '/', [ 'Sys.Audit' ]],
479 additionalProperties
=> 0,
489 return PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
492 __PACKAGE__-
>register_method({
493 name
=> 'set_options',
496 description
=> "Set datacenter options.",
498 check
=> ['perm', '/', [ 'Sys.Modify' ]],
502 additionalProperties
=> 0,
503 properties
=> $dc_properties,
505 returns
=> { type
=> "null" },
509 my $filename = 'datacenter.cfg';
511 my $delete = extract_param
($param, 'delete');
515 my $conf = cfs_read_file
($filename);
517 foreach my $opt (keys %$param) {
518 $conf->{$opt} = $param->{$opt};
521 foreach my $opt (PVE
::Tools
::split_list
($delete)) {
522 delete $conf->{$opt};
525 cfs_write_file
($filename, $conf);
528 cfs_lock_file
($filename, undef, $code);
534 __PACKAGE__-
>register_method({
535 name
=> 'get_status',
538 description
=> "Get cluster status information.",
540 check
=> ['perm', '/', [ 'Sys.Audit' ]],
544 additionalProperties
=> 0,
561 # make sure we get current info
562 PVE
::Cluster
::cfs_update
();
564 # we also add info from pmxcfs
565 my $clinfo = PVE
::Cluster
::get_clinfo
();
566 my $members = PVE
::Cluster
::get_members
();
567 my $nodename = PVE
::INotify
::nodename
();
568 my $rrd = PVE
::Cluster
::rrd_dump
();
573 if (my $d = $clinfo->{cluster
}) {
577 nodes
=> $d->{nodes
},
578 version
=> $d->{version
},
580 quorate
=> $d->{quorate
},
584 foreach my $node (keys %$members) {
585 my $d = $members->{$node};
592 'local' => ($node eq $nodename) ?
1 : 0,
593 online
=> $d->{online
},
596 if (my $d = PVE
::API2Tools
::extract_node_stats
($node, $members, $rrd)) {
597 $entry->{level
} = $d->{level
};
604 # fake entry for local node if no cluster defined
605 my $pmxcfs = ($clinfo && $clinfo->{version
}) ?
1 : 0; # pmxcfs online ?
607 my $subinfo = PVE
::INotify
::read_file
('subscription');
608 my $sublevel = $subinfo->{level
} || '';
612 id
=> "node/$nodename",
614 ip
=> scalar(PVE
::Cluster
::remote_node_ip
($nodename)),
623 __PACKAGE__-
>register_method({
627 description
=> "Get next free VMID. If you pass an VMID it will raise an error if the ID is already used.",
628 permissions
=> { user
=> 'all' },
630 additionalProperties
=> 0,
632 vmid
=> get_standard_option
('pve-vmid', {optional
=> 1}),
637 description
=> "The next free VMID.",
642 my $vmlist = PVE
::Cluster
::get_vmlist
() || {};
643 my $idlist = $vmlist->{ids
} || {};
645 if (my $vmid = $param->{vmid
}) {
646 return $vmid if !defined($idlist->{$vmid});
647 raise_param_exc
({ vmid
=> "VM $vmid already exists" });
650 for (my $i = 100; $i < 10000; $i++) {
651 return $i if !defined($idlist->{$i});
654 die "unable to get any free VMID\n";