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 use base
qw(PVE::Daemon);
25 my $cmdline = [$0, @ARGV];
27 my %daemon_options = (restart_on_error
=> 5, stop_wait_time
=> 5);
29 my $daemon = __PACKAGE__-
>new('pvestatd', $cmdline, %daemon_options);
31 my $rpcenv = PVE
::RPCEnvironment-
>init('cli');
33 $rpcenv->init_request();
34 $rpcenv->set_language($ENV{LANG
});
35 $rpcenv->set_user('root@pam');
37 my $nodename = PVE
::INotify
::nodename
();
38 my $restart_request = 0;
43 PVE
::Cluster
::cfs_update
();
49 syslog
('info' , "server closing");
52 1 while (waitpid(-1, POSIX
::WNOHANG
()) > 0);
54 $self->exit_daemon(0);
60 syslog
('info' , "received signal HUP");
65 sub update_node_status
{
67 my ($avg1, $avg5, $avg15) = PVE
::ProcFSTools
::read_loadavg
();
69 my $stat = PVE
::ProcFSTools
::read_proc_stat
();
71 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
73 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
();
75 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
77 my $maxcpu = $cpuinfo->{cpus
};
79 my $subinfo = PVE
::INotify
::read_file
('subscription');
80 my $sublevel = $subinfo->{level
} || '';
82 # traffic from/to physical interface cards
85 foreach my $dev (keys %$netdev) {
86 next if $dev !~ m/^eth\d+$/;
87 $netin += $netdev->{$dev}->{receive
};
88 $netout += $netdev->{$dev}->{transmit
};
91 my $meminfo = PVE
::ProcFSTools
::read_meminfo
();
93 my $dinfo = df
('/', 1); # output is bytes
97 # everything not free is considered to be used
98 my $dused = $dinfo->{blocks
} - $dinfo->{bfree
};
100 my $data = "$uptime:$sublevel:$ctime:$avg1:$maxcpu:$stat->{cpu}:$stat->{wait}:" .
101 "$meminfo->{memtotal}:$meminfo->{memused}:" .
102 "$meminfo->{swaptotal}:$meminfo->{swapused}:" .
103 "$dinfo->{blocks}:$dused:$netin:$netout";
105 PVE
::Cluster
::broadcast_rrd
("pve2-node/$nodename", $data);
112 return if !$opt_debug;
116 my $hostmeminfo = PVE
::ProcFSTools
::read_meminfo
();
118 # to debug, run 'pvestatd -d' and set memtotal here
119 #$hostmeminfo->{memtotal} = int(2*1024*1024*1024/0.8); # you can set this to test
121 my $hostfreemem = $hostmeminfo->{memtotal
} - $hostmeminfo->{memused
};
123 # we try to use about 80% host memory
124 # goal: we want to change memory usage by this amount (positive or negative)
125 my $goal = int($hostmeminfo->{memtotal
}*0.8 - $hostmeminfo->{memused
});
127 my $maxchange = 100*1024*1024;
128 my $res = PVE
::AutoBalloon
::compute_alg1
($vmstatus, $goal, $maxchange);
130 &$log("host goal: $goal free: $hostfreemem total: $hostmeminfo->{memtotal}\n");
132 foreach my $vmid (keys %$vmstatus) {
133 next if !$res->{$vmid};
134 my $d = $vmstatus->{$vmid};
135 my $diff = int($res->{$vmid} - $d->{balloon
});
136 my $absdiff = $diff < 0 ?
-$diff : $diff;
138 &$log("BALLOON $vmid to $res->{$vmid} ($diff)\n");
140 PVE
::QemuServer
::vm_mon_cmd
($vmid, "balloon",
141 value
=> int($res->{$vmid}));
148 sub update_qemu_status
{
152 my $vmstatus = PVE
::QemuServer
::vmstatus
(undef, 1);
154 eval { auto_balloning
($vmstatus); };
155 syslog
('err', "auto ballooning error: $@") if $@;
157 foreach my $vmid (keys %$vmstatus) {
158 my $d = $vmstatus->{$vmid};
160 my $status = $d->{qmpstatus
} || $d->{status
} || 'stopped';
161 my $template = $d->{template
} ?
$d->{template
} : "0";
162 if ($d->{pid
}) { # running
163 $data = "$d->{uptime}:$d->{name}:$status:$template:" .
164 "$ctime:$d->{cpus}:$d->{cpu}:" .
165 "$d->{maxmem}:$d->{mem}:" .
166 "$d->{maxdisk}:$d->{disk}:" .
167 "$d->{netin}:$d->{netout}:" .
168 "$d->{diskread}:$d->{diskwrite}";
170 $data = "0:$d->{name}:$status:$template:$ctime:$d->{cpus}::" .
172 "$d->{maxdisk}:$d->{disk}:" .
175 PVE
::Cluster
::broadcast_rrd
("pve2.3-vm/$vmid", $data);
179 sub find_vzctl_console_pids
{
183 dir_glob_foreach
('/proc', '\d+', sub {
186 my $cmdline = file_read_firstline
("/proc/$pid/cmdline");
189 my @args = split(/\0/, $cmdline);
191 # serach for vzctl console <vmid>
192 return if scalar(@args) != 3;
193 return if $args[1] ne 'console';
194 return if $args[2] !~ m/^\d+$/;
195 return if $args[0] !~ m
|^(/usr/sbin
/)?vzctl
$|;
199 push @{$res->{$vmid}}, $pid;
205 sub remove_stale_openvz_consoles
{
207 my $vmstatus = PVE
::OpenVZ
::vmstatus
();
208 my $pidhash = find_vzctl_console_pids
();
210 foreach my $vmid (keys %$pidhash) {
211 next if defined($vmstatus->{$vmid});
212 syslog
('info', "remove stale vzctl console for CT $vmid");
213 foreach my $pid (@{$pidhash->{$vmid}}) {
219 sub update_openvz_status
{
223 my $vmstatus = PVE
::OpenVZ
::vmstatus
();
225 foreach my $vmid (keys %$vmstatus) {
226 my $d = $vmstatus->{$vmid};
228 if ($d->{status
} eq 'running') { # running
229 $data = "$d->{uptime}:$d->{name}:$d->{status}:0:$ctime:$d->{cpus}:$d->{cpu}:" .
230 "$d->{maxmem}:$d->{mem}:" .
231 "$d->{maxdisk}:$d->{disk}:" .
232 "$d->{netin}:$d->{netout}:" .
233 "$d->{diskread}:$d->{diskwrite}";
235 $data = "0:$d->{name}:$d->{status}:0:$ctime:$d->{cpus}::" .
237 "$d->{maxdisk}:$d->{disk}:" .
240 PVE
::Cluster
::broadcast_rrd
("pve2.3-vm/$vmid", $data);
244 sub update_storage_status
{
246 my $cfg = cfs_read_file
("storage.cfg");
250 my $info = PVE
::Storage
::storage_info
($cfg);
252 foreach my $storeid (keys %$info) {
253 my $d = $info->{$storeid};
254 next if !$d->{active
};
256 # everything not free is considered to be used
257 my $realused = $d->{total
} - $d->{avail
};
259 my $data = "$ctime:$d->{total}:$realused";
261 my $key = "pve2-storage/${nodename}/$storeid";
262 PVE
::Cluster
::broadcast_rrd
($key, $data);
268 # update worker list. This is not really required and
269 # we just call this to make sure that we have a correct
270 # list in case of an unexpected crash.
272 my $tlist = PVE
::RPCEnvironment
::active_workers
();
273 PVE
::Cluster
::broadcast_tasklist
($tlist);
276 syslog
('err', $err) if $err;
279 update_node_status
();
282 syslog
('err', "node status update error: $err") if $err;
285 update_qemu_status
();
288 syslog
('err', "qemu status update error: $err") if $err;
291 update_openvz_status
();
294 syslog
('err', "openvz status update error: $err") if $err;
297 update_storage_status
();
300 syslog
('err', "storage status update error: $err") if $err;
303 remove_stale_openvz_consoles
();
306 syslog
('err', "openvz console cleanup error: $err") if $err;
311 # do not update directly after startup, because install scripts
312 # have a problem with that
316 my $initial_memory_usage;
323 $next_update = time() + $updatetime;
326 my ($ccsec, $cusec) = gettimeofday
();
328 # syslog('info', "start status update");
329 PVE
::Cluster
::cfs_update
();
335 syslog
('err', "status update error: $err");
338 my ($ccsec_end, $cusec_end) = gettimeofday
();
339 my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
341 syslog
('info', sprintf("status update time (%.3f seconds)", $cptime))
347 my $mem = PVE
::ProcFSTools
::read_memory_usage
();
349 if (!defined($initial_memory_usage) || ($cycle < 10)) {
350 $initial_memory_usage = $mem->{resident
};
352 my $diff = $mem->{resident
} - $initial_memory_usage;
353 if ($diff > 5*1024*1024) {
354 syslog
('info', "restarting server after $cycle cycles to " .
355 "reduce memory usage (free $mem->{resident} ($diff) bytes)");
356 $self->restart_daemon();
361 while ((time() < $next_update) &&
362 ($wcount < $updatetime) && # protect against time wrap
363 !$restart_request) { $wcount++; sleep (1); };
365 $self->restart_daemon() if $restart_request;
369 $daemon->register_start_command(__PACKAGE__
);
370 $daemon->register_restart_command(__PACKAGE__
);
371 $daemon->register_stop_command(__PACKAGE__
);
372 $daemon->register_status_command(__PACKAGE__
);
375 start
=> [ __PACKAGE__
, 'start', []],
376 restart
=> [ __PACKAGE__
, 'restart', []],
377 stop
=> [ __PACKAGE__
, 'stop', []],
378 status
=> [ __PACKAGE__
, 'status', [], undef, sub { print shift . "\n";} ],
383 PVE
::CLIHandler
::handle_cmd
($cmddef, $0, $cmd, \
@ARGV, undef, $0);
391 pvestatd - PVE Status Daemon
399 This daemom queries the status of VMs, storages and containers at
400 regular intervals. The result is sent to all nodes in the cluster.
402 =include pve_copyright