5 use POSIX
":sys_wait_h";
8 use Time
::HiRes qw
(gettimeofday
);
9 use PVE
::Tools
qw(dir_glob_foreach file_read_firstline);
13 use PVE
::Cluster
qw(cfs_read_file);
17 use PVE
::RPCEnvironment
;
18 use PVE
::API2
::Subscription
;
21 $SIG{'__WARN__'} = sub {
25 syslog
('warning', "WARNING: %s", $t);
31 $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
33 die "please run as root\n" if $> != 0;
35 my $nodename = PVE
::INotify
::nodename
();
39 if (!GetOptions
('debug' => \
$opt_debug)) {
40 die "USAGE: $0 [--debug]\n";
43 my $opt_pidfile = "/var/run/pvestatd.pid";
47 my $lkfn = "$pidfile.lock";
49 if (!open (FLCK
, ">>$lkfn")) {
50 my $msg = "can't aquire lock on file '$lkfn' - $!";
55 if (!flock (FLCK
, LOCK_EX
|LOCK_NB
)) {
57 my $msg = "can't aquire lock '$lkfn' - $!";
66 if (!open (PIDFH
, ">$pidfile")) {
67 my $msg = "can't open pid file '$pidfile' - $!";
76 lockpidfile
($opt_pidfile);
81 my $restart = $ENV{RESTART_PVESTATD
};
84 open STDIN
, '</dev/null' || die "can't read /dev/null";
85 open STDOUT
, '>/dev/null' || die "can't write /dev/null";
88 if (!$restart && !$opt_debug) {
90 if (!defined ($spid)) {
91 my $msg = "can't put server into background - fork failed";
94 } elsif ($spid) { #parent
99 writepidfile
($opt_pidfile);
101 open STDERR
, '>&STDOUT' || die "can't close STDERR\n";
104 unlink "$opt_pidfile.lock";
105 unlink "$opt_pidfile";
108 $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = sub {
109 syslog
('info' , "server closing");
111 $SIG{INT
} = 'DEFAULT';
114 1 while (waitpid(-1, POSIX
::WNOHANG
()) > 0);
121 PVE
::INotify
::inotify_init
();
126 syslog
('info' , "restarting server");
128 syslog
('info' , "starting server");
135 sub update_node_status
{
137 my ($avg1, $avg5, $avg15) = PVE
::ProcFSTools
::read_loadavg
();
139 my $stat = PVE
::ProcFSTools
::read_proc_stat
();
141 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
143 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
();
145 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
147 my $maxcpu = $cpuinfo->{cpus
};
149 my $subinfo = PVE
::INotify
::read_file
('subscription');
150 my $sublevel = $subinfo->{level
} || '';
152 # traffic from/to physical interface cards
155 foreach my $dev (keys %$netdev) {
156 next if $dev !~ m/^eth\d+$/;
157 $netin += $netdev->{$dev}->{receive
};
158 $netout += $netdev->{$dev}->{transmit
};
161 my $meminfo = PVE
::ProcFSTools
::read_meminfo
();
163 my $dinfo = df
('/', 1); # output is bytes
167 # everything not free is considered to be used
168 my $dused = $dinfo->{blocks
} - $dinfo->{bfree
};
170 my $data = "$uptime:$sublevel:$ctime:$avg1:$maxcpu:$stat->{cpu}:$stat->{wait}:" .
171 "$meminfo->{memtotal}:$meminfo->{memused}:" .
172 "$meminfo->{swaptotal}:$meminfo->{swapused}:" .
173 "$dinfo->{blocks}:$dused:$netin:$netout";
175 PVE
::Cluster
::broadcast_rrd
("pve2-node/$nodename", $data);
182 return if !$opt_debug;
186 my $hostmeminfo = PVE
::ProcFSTools
::read_meminfo
();
188 # to debug, run 'pvestatd -d' and set memtotal here
189 #$hostmeminfo->{memtotal} = int(2*1024*1024*1024/0.8); # you can set this to test
191 my $hostfreemem = $hostmeminfo->{memtotal
} - $hostmeminfo->{memused
};
193 # we try to use about 80% host memory
194 # goal: we want to change memory usage by this amount (positive or negative)
195 my $goal = int($hostmeminfo->{memtotal
}*0.8 - $hostmeminfo->{memused
});
197 my $maxchange = 100*1024*1024;
198 my $res = PVE
::AutoBalloon
::compute_alg1
($vmstatus, $goal, $maxchange);
200 &$log("host goal: $goal free: $hostfreemem total: $hostmeminfo->{memtotal}\n");
202 foreach my $vmid (keys %$vmstatus) {
203 next if !$res->{$vmid};
204 my $d = $vmstatus->{$vmid};
205 my $diff = int($res->{$vmid} - $d->{balloon
});
206 my $absdiff = $diff < 0 ?
-$diff : $diff;
208 &$log("BALLOON $vmid to $res->{$vmid} ($diff)\n");
210 PVE
::QemuServer
::vm_mon_cmd
($vmid, "balloon",
211 value
=> int($res->{$vmid}));
218 sub update_qemu_status
{
222 my $vmstatus = PVE
::QemuServer
::vmstatus
(undef, 1);
224 eval { auto_balloning
($vmstatus); };
225 syslog
('err', "auto ballooning error: $@") if $@;
227 foreach my $vmid (keys %$vmstatus) {
228 my $d = $vmstatus->{$vmid};
230 if ($d->{pid
}) { # running
231 $data = "$d->{uptime}:$d->{name}:$ctime:$d->{cpus}:$d->{cpu}:" .
232 "$d->{maxmem}:$d->{mem}:" .
233 "$d->{maxdisk}:$d->{disk}:" .
234 "$d->{netin}:$d->{netout}:" .
235 "$d->{diskread}:$d->{diskwrite}";
237 $data = "0:$d->{name}:$ctime:$d->{cpus}::" .
239 "$d->{maxdisk}:$d->{disk}:" .
242 PVE
::Cluster
::broadcast_rrd
("pve2-vm/$vmid", $data);
246 sub find_vzctl_console_pids
{
250 dir_glob_foreach
('/proc', '\d+', sub {
253 my $cmdline = file_read_firstline
("/proc/$pid/cmdline");
256 my @args = split(/\0/, $cmdline);
258 # serach for vzctl console <vmid>
259 return if scalar(@args) != 3;
260 return if $args[1] ne 'console';
261 return if $args[2] !~ m/^\d+$/;
262 return if $args[0] !~ m
|^(/usr/sbin
/)?vzctl
$|;
266 push @{$res->{$vmid}}, $pid;
271 sub remove_stale_openvz_consoles
{
273 my $vmstatus = PVE
::OpenVZ
::vmstatus
();
274 my $pidhash = find_vzctl_console_pids
();
276 foreach my $vmid (keys %$pidhash) {
277 next if defined($vmstatus->{$vmid});
278 syslog
('info', "remove stale vzctl console for CT $vmid");
279 foreach my $pid (@{$pidhash->{$vmid}}) {
285 sub update_openvz_status
{
289 my $vmstatus = PVE
::OpenVZ
::vmstatus
();
291 foreach my $vmid (keys %$vmstatus) {
292 my $d = $vmstatus->{$vmid};
294 if ($d->{status
} eq 'running') { # running
295 $data = "$d->{uptime}:$d->{name}:$ctime:$d->{cpus}:$d->{cpu}:" .
296 "$d->{maxmem}:$d->{mem}:" .
297 "$d->{maxdisk}:$d->{disk}:" .
298 "$d->{netin}:$d->{netout}:" .
299 "$d->{diskread}:$d->{diskwrite}";
301 $data = "0:$d->{name}:$ctime:$d->{cpus}::" .
303 "$d->{maxdisk}:$d->{disk}:" .
306 PVE
::Cluster
::broadcast_rrd
("pve2-vm/$vmid", $data);
310 sub update_storage_status
{
312 my $cfg = cfs_read_file
("storage.cfg");
316 my $info = PVE
::Storage
::storage_info
($cfg);
318 foreach my $storeid (keys %$info) {
319 my $d = $info->{$storeid};
320 next if !$d->{active
};
322 # everything not free is considered to be used
323 my $realused = $d->{total
} - $d->{avail
};
325 my $data = "$ctime:$d->{total}:$realused";
327 my $key = "pve2-storage/${nodename}/$storeid";
328 PVE
::Cluster
::broadcast_rrd
($key, $data);
334 # update worker list. This is not really required and
335 # we just call this to make sure that we have a correct
336 # list in case of an unexpected crash.
338 my $tlist = PVE
::RPCEnvironment
::active_workers
();
339 PVE
::Cluster
::broadcast_tasklist
($tlist);
342 syslog
('err', $err) if $err;
345 update_node_status
();
348 syslog
('err', "node status update error: $err") if $err;
351 update_qemu_status
();
354 syslog
('err', "qemu status update error: $err") if $err;
357 update_openvz_status
();
360 syslog
('err', "openvz status update error: $err") if $err;
363 update_storage_status
();
366 syslog
('err', "storage status update error: $err") if $err;
369 remove_stale_openvz_consoles
();
372 syslog
('err', "openvz console cleanup error: $err") if $err;
377 # do not update directly after startup, because install scripts
378 # have a problem with that
382 my $commandline = [$0, @ARGV];
387 my $waittime = shift;
389 syslog
('info', "server shutdown (restart)");
391 $ENV{RESTART_PVESTATD
} = 1;
393 sleep($waittime) if $waittime; # avoid high server load due to restarts
395 exec (@$commandline);
396 exit (-1); # never reached?
399 my $initial_memory_usage;
404 $next_update = time() + $updatetime;
407 my ($ccsec, $cusec) = gettimeofday
();
410 # syslog('info', "start status update");
411 PVE
::Cluster
::cfs_update
();
417 syslog
('err', "status update error: $err");
420 my ($ccsec_end, $cusec_end) = gettimeofday
();
421 my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
423 syslog
('info', sprintf("status update time (%.3f seconds)", $cptime))
429 my $mem = PVE
::ProcFSTools
::read_memory_usage
();
431 if (!defined($initial_memory_usage) || ($cycle < 10)) {
432 $initial_memory_usage = $mem->{resident
};
434 my $diff = $mem->{resident
} - $initial_memory_usage;
435 if ($diff > 5*1024*1024) {
436 syslog
('info', "restarting server after $cycle cycles to " .
437 "reduce memory usage (free $mem->{resident} ($diff) bytes)");
443 while ((time() < $next_update) &&
444 ($wcount < $updatetime) && # protect against time wrap
445 !$reload_config) { $wcount++; sleep (1); };
451 syslog
('err', "ERROR: $err");
463 pvestatd - PVE Status Daemon
471 Documentation is available at www.proxmox.com