6 use POSIX
":sys_wait_h";
9 use Time
::HiRes qw
(gettimeofday
);
10 use PVE
::Tools
qw(dir_glob_foreach file_read_firstline);
14 use PVE
::Cluster
qw(cfs_read_file);
18 use PVE
::RPCEnvironment
;
19 use PVE
::API2
::Subscription
;
22 $SIG{'__WARN__'} = sub {
26 syslog
('warning', "WARNING: %s", $t);
32 $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
34 die "please run as root\n" if $> != 0;
36 my $nodename = PVE
::INotify
::nodename
();
40 if (!GetOptions
('debug' => \
$opt_debug)) {
41 die "USAGE: $0 [--debug]\n";
44 my $opt_pidfile = "/var/run/pvestatd.pid";
48 my $lkfn = "$pidfile.lock";
50 if (!open (FLCK
, ">>$lkfn")) {
51 my $msg = "can't aquire lock on file '$lkfn' - $!";
56 if (!flock (FLCK
, LOCK_EX
|LOCK_NB
)) {
58 my $msg = "can't aquire lock '$lkfn' - $!";
67 if (!open (PIDFH
, ">$pidfile")) {
68 my $msg = "can't open pid file '$pidfile' - $!";
77 lockpidfile
($opt_pidfile);
82 my $restart = $ENV{RESTART_PVESTATD
};
85 open STDIN
, '</dev/null' || die "can't read /dev/null";
86 open STDOUT
, '>/dev/null' || die "can't write /dev/null";
89 if (!$restart && !$opt_debug) {
91 if (!defined ($spid)) {
92 my $msg = "can't put server into background - fork failed";
95 } elsif ($spid) { #parent
100 writepidfile
($opt_pidfile);
102 open STDERR
, '>&STDOUT' || die "can't close STDERR\n";
105 unlink "$opt_pidfile.lock";
106 unlink "$opt_pidfile";
109 $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = sub {
110 syslog
('info' , "server closing");
112 $SIG{INT
} = 'DEFAULT';
115 1 while (waitpid(-1, POSIX
::WNOHANG
()) > 0);
122 PVE
::INotify
::inotify_init
();
127 syslog
('info' , "restarting server");
129 syslog
('info' , "starting server");
136 sub update_node_status
{
138 my ($avg1, $avg5, $avg15) = PVE
::ProcFSTools
::read_loadavg
();
140 my $stat = PVE
::ProcFSTools
::read_proc_stat
();
142 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
144 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
();
146 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
148 my $maxcpu = $cpuinfo->{cpus
};
150 my $subinfo = PVE
::INotify
::read_file
('subscription');
151 my $sublevel = $subinfo->{level
} || '';
153 # traffic from/to physical interface cards
156 foreach my $dev (keys %$netdev) {
157 next if $dev !~ m/^eth\d+$/;
158 $netin += $netdev->{$dev}->{receive
};
159 $netout += $netdev->{$dev}->{transmit
};
162 my $meminfo = PVE
::ProcFSTools
::read_meminfo
();
164 my $dinfo = df
('/', 1); # output is bytes
168 # everything not free is considered to be used
169 my $dused = $dinfo->{blocks
} - $dinfo->{bfree
};
171 my $data = "$uptime:$sublevel:$ctime:$avg1:$maxcpu:$stat->{cpu}:$stat->{wait}:" .
172 "$meminfo->{memtotal}:$meminfo->{memused}:" .
173 "$meminfo->{swaptotal}:$meminfo->{swapused}:" .
174 "$dinfo->{blocks}:$dused:$netin:$netout";
176 PVE
::Cluster
::broadcast_rrd
("pve2-node/$nodename", $data);
183 return if !$opt_debug;
187 my $hostmeminfo = PVE
::ProcFSTools
::read_meminfo
();
189 # to debug, run 'pvestatd -d' and set memtotal here
190 #$hostmeminfo->{memtotal} = int(2*1024*1024*1024/0.8); # you can set this to test
192 my $hostfreemem = $hostmeminfo->{memtotal
} - $hostmeminfo->{memused
};
194 # we try to use about 80% host memory
195 # goal: we want to change memory usage by this amount (positive or negative)
196 my $goal = int($hostmeminfo->{memtotal
}*0.8 - $hostmeminfo->{memused
});
198 my $maxchange = 100*1024*1024;
199 my $res = PVE
::AutoBalloon
::compute_alg1
($vmstatus, $goal, $maxchange);
201 &$log("host goal: $goal free: $hostfreemem total: $hostmeminfo->{memtotal}\n");
203 foreach my $vmid (keys %$vmstatus) {
204 next if !$res->{$vmid};
205 my $d = $vmstatus->{$vmid};
206 my $diff = int($res->{$vmid} - $d->{balloon
});
207 my $absdiff = $diff < 0 ?
-$diff : $diff;
209 &$log("BALLOON $vmid to $res->{$vmid} ($diff)\n");
211 PVE
::QemuServer
::vm_mon_cmd
($vmid, "balloon",
212 value
=> int($res->{$vmid}));
219 sub update_qemu_status
{
223 my $vmstatus = PVE
::QemuServer
::vmstatus
(undef, 1);
225 eval { auto_balloning
($vmstatus); };
226 syslog
('err', "auto ballooning error: $@") if $@;
228 foreach my $vmid (keys %$vmstatus) {
229 my $d = $vmstatus->{$vmid};
231 my $status = $d->{qmpstatus
} || $d->{status
} || 'stopped';
232 my $template = $d->{template
} ?
$d->{template
} : "0";
233 if ($d->{pid
}) { # running
234 $data = "$d->{uptime}:$d->{name}:$status:$template:" .
235 "$ctime:$d->{cpus}:$d->{cpu}:" .
236 "$d->{maxmem}:$d->{mem}:" .
237 "$d->{maxdisk}:$d->{disk}:" .
238 "$d->{netin}:$d->{netout}:" .
239 "$d->{diskread}:$d->{diskwrite}";
241 $data = "0:$d->{name}:$status:$template:$ctime:$d->{cpus}::" .
243 "$d->{maxdisk}:$d->{disk}:" .
246 PVE
::Cluster
::broadcast_rrd
("pve2.3-vm/$vmid", $data);
250 sub find_vzctl_console_pids
{
254 dir_glob_foreach
('/proc', '\d+', sub {
257 my $cmdline = file_read_firstline
("/proc/$pid/cmdline");
260 my @args = split(/\0/, $cmdline);
262 # serach for vzctl console <vmid>
263 return if scalar(@args) != 3;
264 return if $args[1] ne 'console';
265 return if $args[2] !~ m/^\d+$/;
266 return if $args[0] !~ m
|^(/usr/sbin
/)?vzctl
$|;
270 push @{$res->{$vmid}}, $pid;
275 sub remove_stale_openvz_consoles
{
277 my $vmstatus = PVE
::OpenVZ
::vmstatus
();
278 my $pidhash = find_vzctl_console_pids
();
280 foreach my $vmid (keys %$pidhash) {
281 next if defined($vmstatus->{$vmid});
282 syslog
('info', "remove stale vzctl console for CT $vmid");
283 foreach my $pid (@{$pidhash->{$vmid}}) {
289 sub update_openvz_status
{
293 my $vmstatus = PVE
::OpenVZ
::vmstatus
();
295 foreach my $vmid (keys %$vmstatus) {
296 my $d = $vmstatus->{$vmid};
298 if ($d->{status
} eq 'running') { # running
299 $data = "$d->{uptime}:$d->{name}:$d->{status}:0:$ctime:$d->{cpus}:$d->{cpu}:" .
300 "$d->{maxmem}:$d->{mem}:" .
301 "$d->{maxdisk}:$d->{disk}:" .
302 "$d->{netin}:$d->{netout}:" .
303 "$d->{diskread}:$d->{diskwrite}";
305 $data = "0:$d->{name}:$d->{status}:0:$ctime:$d->{cpus}::" .
307 "$d->{maxdisk}:$d->{disk}:" .
310 PVE
::Cluster
::broadcast_rrd
("pve2.3-vm/$vmid", $data);
314 sub update_storage_status
{
316 my $cfg = cfs_read_file
("storage.cfg");
320 my $info = PVE
::Storage
::storage_info
($cfg);
322 foreach my $storeid (keys %$info) {
323 my $d = $info->{$storeid};
324 next if !$d->{active
};
326 # everything not free is considered to be used
327 my $realused = $d->{total
} - $d->{avail
};
329 my $data = "$ctime:$d->{total}:$realused";
331 my $key = "pve2-storage/${nodename}/$storeid";
332 PVE
::Cluster
::broadcast_rrd
($key, $data);
338 # update worker list. This is not really required and
339 # we just call this to make sure that we have a correct
340 # list in case of an unexpected crash.
342 my $tlist = PVE
::RPCEnvironment
::active_workers
();
343 PVE
::Cluster
::broadcast_tasklist
($tlist);
346 syslog
('err', $err) if $err;
349 update_node_status
();
352 syslog
('err', "node status update error: $err") if $err;
355 update_qemu_status
();
358 syslog
('err', "qemu status update error: $err") if $err;
361 update_openvz_status
();
364 syslog
('err', "openvz status update error: $err") if $err;
367 update_storage_status
();
370 syslog
('err', "storage status update error: $err") if $err;
373 remove_stale_openvz_consoles
();
376 syslog
('err', "openvz console cleanup error: $err") if $err;
381 # do not update directly after startup, because install scripts
382 # have a problem with that
386 my $commandline = [$0, @ARGV];
391 my $waittime = shift;
393 syslog
('info', "server shutdown (restart)");
395 $ENV{RESTART_PVESTATD
} = 1;
397 sleep($waittime) if $waittime; # avoid high server load due to restarts
399 exec (@$commandline);
400 exit (-1); # never reached?
403 my $initial_memory_usage;
408 $next_update = time() + $updatetime;
411 my ($ccsec, $cusec) = gettimeofday
();
414 # syslog('info', "start status update");
415 PVE
::Cluster
::cfs_update
();
421 syslog
('err', "status update error: $err");
424 my ($ccsec_end, $cusec_end) = gettimeofday
();
425 my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
427 syslog
('info', sprintf("status update time (%.3f seconds)", $cptime))
433 my $mem = PVE
::ProcFSTools
::read_memory_usage
();
435 if (!defined($initial_memory_usage) || ($cycle < 10)) {
436 $initial_memory_usage = $mem->{resident
};
438 my $diff = $mem->{resident
} - $initial_memory_usage;
439 if ($diff > 5*1024*1024) {
440 syslog
('info', "restarting server after $cycle cycles to " .
441 "reduce memory usage (free $mem->{resident} ($diff) bytes)");
447 while ((time() < $next_update) &&
448 ($wcount < $updatetime) && # protect against time wrap
449 !$reload_config) { $wcount++; sleep (1); };
455 syslog
('err', "ERROR: $err");
467 pvestatd - PVE Status Daemon
475 Documentation is available at www.proxmox.com