]> git.proxmox.com Git - pve-manager.git/blame - bin/pvestatd
bump version to 2.3-2
[pve-manager.git] / bin / pvestatd
CommitLineData
aff192e6
DM
1#!/usr/bin/perl -w
2
3use strict;
4use PVE::SafeSyslog;
5use POSIX ":sys_wait_h";
6use Fcntl ':flock';
7use Getopt::Long;
8use Time::HiRes qw (gettimeofday);
f9d4fc64 9use PVE::Tools qw(dir_glob_foreach file_read_firstline);
aff192e6
DM
10use PVE::ProcFSTools;
11use Filesys::Df;
12use PVE::INotify;
13use PVE::Cluster qw(cfs_read_file);
14use PVE::Storage;
15use PVE::QemuServer;
b3409356 16use PVE::OpenVZ;
aff192e6 17use PVE::RPCEnvironment;
16b69b6c 18use PVE::API2::Subscription;
aff192e6
DM
19
20$SIG{'__WARN__'} = sub {
21 my $err = $@;
22 my $t = $_[0];
23 chomp $t;
24 syslog('warning', "WARNING: %s", $t);
25 $@ = $err;
26};
27
28initlog('pvestatd');
29
30$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
31
32die "please run as root\n" if $> != 0;
33
34my $nodename = PVE::INotify::nodename();
35
36my $opt_debug;
37
38if (!GetOptions ('debug' => \$opt_debug)) {
39 die "USAGE: $0 [--debug]\n";
40}
41
42my $opt_pidfile = "/var/run/pvestatd.pid";
43
44sub lockpidfile {
45 my $pidfile = shift;
46 my $lkfn = "$pidfile.lock";
47
48 if (!open (FLCK, ">>$lkfn")) {
49 my $msg = "can't aquire lock on file '$lkfn' - $!";
50 syslog ('err', $msg);
51 die "ERROR: $msg\n";
52 }
53
54 if (!flock (FLCK, LOCK_EX|LOCK_NB)) {
55 close (FLCK);
56 my $msg = "can't aquire lock '$lkfn' - $!";
57 syslog ('err', $msg);
58 die "ERROR: $msg\n";
59 }
60}
61
62sub writepidfile {
63 my $pidfile = shift;
64
65 if (!open (PIDFH, ">$pidfile")) {
66 my $msg = "can't open pid file '$pidfile' - $!";
67 syslog ('err', $msg);
68 die "ERROR: $msg\n";
69 }
70 print PIDFH "$$\n";
71 close (PIDFH);
72}
73
74# try to get the lock
75lockpidfile($opt_pidfile);
76
77# run in background
78my $spid;
79
80my $restart = $ENV{RESTART_PVESTATD};
81
82if (!$opt_debug) {
83 open STDIN, '</dev/null' || die "can't read /dev/null";
84 open STDOUT, '>/dev/null' || die "can't write /dev/null";
85}
86
87if (!$restart && !$opt_debug) {
88 $spid = fork();
89 if (!defined ($spid)) {
90 my $msg = "can't put server into background - fork failed";
91 syslog('err', $msg);
92 die "ERROR: $msg\n";
93 } elsif ($spid) { #parent
94 exit (0);
95 }
96}
97
98writepidfile($opt_pidfile);
99
100open STDERR, '>&STDOUT' || die "can't close STDERR\n";
101
102sub cleanup {
103 unlink "$opt_pidfile.lock";
104 unlink "$opt_pidfile";
105}
106
107$SIG{INT} = $SIG{TERM} = $SIG{QUIT} = sub {
108 syslog('info' , "server closing");
109
110 $SIG{INT} = 'DEFAULT';
111
112 # wait for children
113 1 while (waitpid(-1, POSIX::WNOHANG()) > 0);
114
115 cleanup();
116
117 exit (0);
118};
119
120PVE::INotify::inotify_init();
121
122my $reload_config;
123
124if ($restart) {
125 syslog('info' , "restarting server");
126} else {
127 syslog('info' , "starting server");
128}
129
130$SIG{HUP} = sub {
131 $reload_config = 1;
132};
133
134sub update_node_status {
135
136 my ($avg1, $avg5, $avg15) = PVE::ProcFSTools::read_loadavg();
137
138 my $stat = PVE::ProcFSTools::read_proc_stat();
139
140 my $netdev = PVE::ProcFSTools::read_proc_net_dev();
141
142 my ($uptime) = PVE::ProcFSTools::read_proc_uptime();
143
144 my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
145
146 my $maxcpu = $cpuinfo->{cpus};
147
16b69b6c
DM
148 my $subinfo = PVE::INotify::read_file('subscription');
149 my $sublevel = $subinfo->{level} || '';
150
aff192e6
DM
151 # traffic from/to physical interface cards
152 my $netin = 0;
153 my $netout = 0;
154 foreach my $dev (keys %$netdev) {
155 next if $dev !~ m/^eth\d+$/;
156 $netin += $netdev->{$dev}->{receive};
157 $netout += $netdev->{$dev}->{transmit};
158 }
159
160 my $meminfo = PVE::ProcFSTools::read_meminfo();
161
162 my $dinfo = df('/', 1); # output is bytes
163
164 my $ctime = time();
165
166 # everything not free is considered to be used
167 my $dused = $dinfo->{blocks} - $dinfo->{bfree};
168
16b69b6c 169 my $data = "$uptime:$sublevel:$ctime:$avg1:$maxcpu:$stat->{cpu}:$stat->{wait}:" .
aff192e6
DM
170 "$meminfo->{memtotal}:$meminfo->{memused}:" .
171 "$meminfo->{swaptotal}:$meminfo->{swapused}:" .
172 "$dinfo->{blocks}:$dused:$netin:$netout";
173
174 PVE::Cluster::broadcast_rrd("pve2-node/$nodename", $data);
175}
176
177sub update_qemu_status {
178
179 my $ctime = time();
180
cbb20c6e 181 my $vmstatus = PVE::QemuServer::vmstatus(undef, 1);
aff192e6
DM
182
183 foreach my $vmid (keys %$vmstatus) {
184 my $d = $vmstatus->{$vmid};
185 my $data;
186 if ($d->{pid}) { # running
187 $data = "$d->{uptime}:$d->{name}:$ctime:$d->{cpus}:$d->{cpu}:" .
188 "$d->{maxmem}:$d->{mem}:" .
189 "$d->{maxdisk}:$d->{disk}:" .
190 "$d->{netin}:$d->{netout}:" .
191 "$d->{diskread}:$d->{diskwrite}";
192 } else {
193 $data = "0:$d->{name}:$ctime:$d->{cpus}::" .
194 "$d->{maxmem}::" .
195 "$d->{maxdisk}:$d->{disk}:" .
196 ":::";
197 }
198 PVE::Cluster::broadcast_rrd("pve2-vm/$vmid", $data);
199 }
200}
201
f9d4fc64
DM
202sub find_vzctl_console_pids {
203
204 my $res = {};
205
206 dir_glob_foreach('/proc', '\d+', sub {
207 my ($pid) = @_;
208
209 my $cmdline = file_read_firstline("/proc/$pid/cmdline");
210 return if !$cmdline;
211
212 my @args = split(/\0/, $cmdline);
213
214 # serach for vzctl console <vmid>
215 return if scalar(@args) != 3;
216 return if $args[1] ne 'console';
217 return if $args[2] !~ m/^\d+$/;
218 return if $args[0] !~ m|^(/usr/sbin/)?vzctl$|;
219
220 my $vmid = $args[2];
221
222 push @{$res->{$vmid}}, $pid;
223 });
224
225 return $res;
226}
227sub remove_stale_openvz_consoles {
228
229 my $vmstatus = PVE::OpenVZ::vmstatus();
230 my $pidhash = find_vzctl_console_pids();
231
232 foreach my $vmid (keys %$pidhash) {
233 next if defined($vmstatus->{$vmid});
234 syslog('info', "remove stale vzctl console for CT $vmid");
235 foreach my $pid (@{$pidhash->{$vmid}}) {
236 kill(9, $pid);
237 }
238 }
239}
240
b3409356
DM
241sub update_openvz_status {
242
243 my $ctime = time();
244
245 my $vmstatus = PVE::OpenVZ::vmstatus();
246
247 foreach my $vmid (keys %$vmstatus) {
248 my $d = $vmstatus->{$vmid};
249 my $data;
250 if ($d->{status} eq 'running') { # running
251 $data = "$d->{uptime}:$d->{name}:$ctime:$d->{cpus}:$d->{cpu}:" .
252 "$d->{maxmem}:$d->{mem}:" .
253 "$d->{maxdisk}:$d->{disk}:" .
254 "$d->{netin}:$d->{netout}:" .
255 "$d->{diskread}:$d->{diskwrite}";
256 } else {
257 $data = "0:$d->{name}:$ctime:$d->{cpus}::" .
258 "$d->{maxmem}::" .
259 "$d->{maxdisk}:$d->{disk}:" .
260 ":::";
261 }
262 PVE::Cluster::broadcast_rrd("pve2-vm/$vmid", $data);
263 }
264}
265
aff192e6
DM
266sub update_storage_status {
267
268 my $cfg = cfs_read_file("storage.cfg");
269
270 my $ctime = time();
271
272 my $info = PVE::Storage::storage_info($cfg);
273
274 foreach my $storeid (keys %$info) {
275 my $d = $info->{$storeid};
276 next if !$d->{active};
277
278 # everything not free is considered to be used
279 my $realused = $d->{total} - $d->{avail};
280
281 my $data = "$ctime:$d->{total}:$realused";
282
283 my $key = "pve2-storage/${nodename}/$storeid";
284 PVE::Cluster::broadcast_rrd($key, $data);
285 }
286}
287
288sub update_status {
289
290 # update worker list. This is not really required and
291 # we just call this to make sure that we have a correct
292 # list in case of an unexpected crash.
293 eval {
294 my $tlist = PVE::RPCEnvironment::active_workers();
295 PVE::Cluster::broadcast_tasklist($tlist);
296 };
297 my $err = $@;
298 syslog('err', $err) if $err;
299
300 eval {
301 update_node_status();
302 };
303 $err = $@;
304 syslog('err', "node status update error: $err") if $err;
305
306 eval {
307 update_qemu_status();
308 };
309 $err = $@;
310 syslog('err', "qemu status update error: $err") if $err;
311
b3409356
DM
312 eval {
313 update_openvz_status();
314 };
315 $err = $@;
316 syslog('err', "openvz status update error: $err") if $err;
317
aff192e6
DM
318 eval {
319 update_storage_status();
320 };
321 $err = $@;
322 syslog('err', "storage status update error: $err") if $err;
f9d4fc64
DM
323
324 eval {
325 remove_stale_openvz_consoles();
326 };
327 $err = $@;
328 syslog('err', "openvz console cleanup error: $err") if $err;
aff192e6
DM
329}
330
331my $next_update = 0;
332
333# do not update directly after startup, because install scripts
334# have a problem with that
335my $cycle = 0;
336my $updatetime = 10;
337
338my $commandline = [$0, @ARGV];
339
340$0 = "pvestatd";
341
342sub restart_server {
343 my $waittime = shift;
344
345 syslog('info', "server shutdown (restart)");
346
347 $ENV{RESTART_PVESTATD} = 1;
348
349 sleep($waittime) if $waittime; # avoid high server load due to restarts
350
351 exec (@$commandline);
352 exit (-1); # never reached?
353}
354
350b3b46
DM
355my $initial_memory_usage;
356
aff192e6
DM
357for (;;) { # forever
358
359 eval {
360 $next_update = time() + $updatetime;
361
362 if ($cycle) {
363 my ($ccsec, $cusec) = gettimeofday ();
364 eval {
365 $reload_config = 0;
9b0aba10 366 # syslog('info', "start status update");
aff192e6
DM
367 PVE::Cluster::cfs_update();
368 update_status();
369 };
370 my $err = $@;
371
372 if ($err) {
373 syslog('err', "status update error: $err");
374 }
375
376 my ($ccsec_end, $cusec_end) = gettimeofday ();
377 my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
378
9b0aba10
DM
379 syslog('info', sprintf("status update time (%.3f seconds)", $cptime))
380 if ($cptime > 5);
aff192e6
DM
381 }
382
383 $cycle++;
384
385 my $mem = PVE::ProcFSTools::read_memory_usage();
386
9dbdda49 387 if (!defined($initial_memory_usage) || ($cycle < 10)) {
350b3b46
DM
388 $initial_memory_usage = $mem->{resident};
389 } else {
390 my $diff = $mem->{resident} - $initial_memory_usage;
391 if ($diff > 5*1024*1024) {
392 syslog ('info', "restarting server after $cycle cycles to " .
393 "reduce memory usage (free $mem->{resident} ($diff) bytes)");
394 restart_server ();
395 }
aff192e6
DM
396 }
397
398 my $wcount = 0;
399 while ((time() < $next_update) &&
400 ($wcount < $updatetime) && # protect against time wrap
401 !$reload_config) { $wcount++; sleep (1); };
402 };
403
404 my $err = $@;
405
406 if ($err) {
407 syslog ('err', "ERROR: $err");
408 restart_server(5);
409 exit (0);
410 }
411}
412
413exit (0);
414
415__END__
416
417=head1 NAME
418
419pvestatd - PVE Status Daemon
420
421=head1 SYNOPSIS
422
423pvestatd
424
425=head1 DESCRIPTION
426
427Documentation is available at www.proxmox.com
428
429
430
431
432