virt-ping
[virt-tools.git] / tools / virt-uname.pl
1 #!/usr/bin/perl -w
2 # virt-tools
3 # Copyright (C) 2009 Red Hat Inc.
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 use strict;
20
21 use Net::SNMP;
22 use Sys::Virt;
23 use Pod::Usage;
24 use Getopt::Long;
25 use Locale::TextDomain 'virt-tools';
26
27 =encoding utf8
28
29 =head1 NAME
30
31 virt-ps, virt-ping, virt-uname, virt-uptime - virtual machine information and statistics
32
33 =head1 SYNOPSIS
34
35  virt-ifconfig [--options] [domname]
36
37  virt-ps [--options] [domname]
38
39  virt-ping [--options] [domname]
40
41  virt-uname [--options] [domname]
42
43  virt-uptime [--options] [domname]
44
45 =head1 COMMON OPTIONS
46
47 All the tools take either a single C<domname> parameter, which is the
48 name of the virtual machine as known to libvirt (C<virsh list>), or no
49 parameter in which case they operate on all currently running guests.
50
51 I<Note:> You must install the C<virt-tools-guest> package in each
52 Linux guest, otherwise these programs will not work.
53
54 There are some common options which can be supplied to any tool:
55
56 =over 4
57
58 =cut
59
60 my $help;
61
62 =item B<--help>
63
64 Display brief help.
65
66 =cut
67
68 my $version;
69
70 =item B<--version>
71
72 Display version number and exit.
73
74 =cut
75
76 my $uri;
77
78 =item B<--connect URI> | B<-c URI>
79
80 If using libvirt, connect to the given I<URI>.  If omitted, then we
81 connect to the default libvirt hypervisor.
82
83 =cut
84
85 my $csv;
86
87 =item B<--csv>
88
89 Write out the results in CSV format (comma-separated values).  This
90 format can be imported easily into databases and spreadsheets, but
91 read L</NOTE ABOUT CSV FORMAT> below.
92
93 =cut
94
95 my $verbose;
96
97 =item B<--verbose> | B<-v>
98
99 Enable verbose messages, useful for debugging.
100
101 =back
102
103 =cut
104
105 GetOptions ("help|?" => \$help,
106             "version" => \$version,
107             "connect|c=s" => \$uri,
108             "csv" => \$csv,
109             "verbose|v" => \$verbose,
110     ) or pod2usage (2);
111 pod2usage (1) if $help;
112 if ($version) {
113     print "@PACKAGE_STRING@\n";
114     exit
115 }
116
117 my %subcommands = (
118     "virt-ps" => [ \&do_ps, \&title_ps ],
119     "virt-ping" => [ \&do_ping, \&title_ping ],
120     "virt-uname" => [ \&do_uname, \&title_uname ],
121     "virt-uptime" => [ \&do_uptime, \&title_uptime ],
122 );
123
124 # Which subcommand?
125 my ($do_it, $title_it);
126 foreach (keys %subcommands) {
127     if ($0 =~ /$_/) {
128         print STDERR "subcommand = $_\n" if $verbose;
129         $do_it = $subcommands{$_}->[0];
130         $title_it = $subcommands{$_}->[1];
131         last;
132     }
133 }
134 die "$0: cannot determine which sub-command to run\n" unless $do_it;
135
136 # If we are being run from a local directory, add that directory to
137 # the path, so we can be run from the source directory without being
138 # installed.
139 if (substr ($0, 0, 1) ne "/") {
140     $_ = $0;
141     s{/[^/]+$}{};
142     $ENV{PATH} = "$_:$ENV{PATH}"; # XXX Windows?
143     print STDERR "PATH set to $ENV{PATH}\n" if $verbose;
144 }
145
146 our $errors = 0;
147
148 # Do we have named guests?
149 if (@ARGV == 0) {
150     my $conn;
151
152     if ($uri) {
153         $conn = Sys::Virt->new (readonly => 1, address => $uri);
154     } else {
155         $conn = Sys::Virt->new (readonly => 1);
156     }
157
158     # Ignore inactive domains.
159     my @doms = $conn->list_domains ();
160
161     my @domnames = map { $_->get_name () } @doms;
162
163     if (@domnames) {
164         &$title_it ();
165         foreach (@domnames) {
166             get_and_do_it ($_);
167         }
168     }
169 } else {
170     &$title_it ();
171     foreach (@ARGV) {
172         get_and_do_it ($_);
173     }
174 }
175
176 sub get_and_do_it
177 {
178     # Turn any errors into warnings.
179     eval {
180         my ($key, $transport);
181         $key = get_key ($_);
182         $transport = get_transport ($_);
183         &$do_it ($_, $key, $transport);
184     };
185     if ($@) {
186         $errors++;
187         warn "$@";
188     }
189 }
190
191 print STDERR "errors = $errors\n" if $verbose;
192
193 exit ($errors == 0 ? 0 : 1);
194
195 # virt-ifconfig is implemented as a separate program.
196
197 =head1 virt-ifconfig
198
199 C<virt-ifconfig> displays the IP address of the guest.
200
201 =head1 virt-ps
202
203 C<virt-ps> displays the process list of the guest.
204
205 =cut
206
207 sub title_ps
208 {
209     print_row (__"Guest");
210 }
211
212 sub do_ps
213 {
214     my $domname = shift;
215     my $key = shift;
216     my $transport = shift;
217
218
219
220 }
221
222 =head1 virt-ping
223
224 C<virt-ping> pings the guest by making an empty virt-tools request,
225 and checking that it replies.  This can be used as a simple test that
226 virt-tools is available and working inside the guest.
227
228 =cut
229
230 sub title_ping
231 {
232     print_row (__"Guest", __"Status");
233 }
234
235 sub do_ping
236 {
237     my $domname = shift;
238     my $key = shift;
239     my $transport = shift;
240
241     my $session = get_snmp_session ($key, $transport);
242     my $sysUpTime = "1.3.6.1.2.1.1.3.0";
243     my $r = $session->get_request (-varbindlist => [$sysUpTime])
244         or die __x("SNMP error: {e}", $session->error);
245     print STDERR "ping: sysUpTime = $r->{$sysUpTime}\n" if $verbose;
246     print_row ($domname, "ok");
247     $session->close;
248 }
249
250 =head1 virt-uname
251
252 C<virt-uname> displays the system information (kernel version etc) of
253 the guest.
254
255 =cut
256
257 sub title_uname
258 {
259     print_row (__"Guest", __"System name");
260 }
261
262 sub do_uname
263 {
264     my $domname = shift;
265     my $key = shift;
266     my $transport = shift;
267
268     my $session = get_snmp_session ($key, $transport);
269     my $sysDescr = "1.3.6.1.2.1.1.1.0";
270     my $r = $session->get_request (-varbindlist => [$sysDescr])
271         or die __x("SNMP error: {e}", $session->error);
272     print_row ($domname, $r->{$sysDescr});
273     $session->close;
274 }
275
276 =head1 virt-uptime
277
278 C<virt-uptime> displays the uptime of the guest
279
280 =cut
281
282 sub title_uptime
283 {
284     print_row (__"Guest", __"Uptime");
285 }
286
287 sub do_uptime
288 {
289     my $domname = shift;
290     my $key = shift;
291     my $transport = shift;
292
293     my $session = get_snmp_session ($key, $transport);
294     my $sysUpTime = "1.3.6.1.2.1.1.3.0";
295     my $r = $session->get_request (-varbindlist => [$sysUpTime])
296         or die __x("SNMP error: {e}", $session->error);
297     print_row ($domname, $r->{$sysUpTime});
298     $session->close;
299 }
300
301 sub print_row
302 {
303     my @fields = @_;
304
305     local $_;
306     my $comma = 0;
307
308     foreach (@fields) {
309         if (!$csv) {
310             printf "%-16s ", $_
311         } else {
312             print "," if $comma;
313             $comma = 1;
314
315             # XXX Use Text::CSV here.
316             if ($_ =~ /"/) {
317                 s/"/""/;
318                 printf "\"%s\"", $_;
319             } elsif ($_ =~ /,/ || $_ =~ /\n/) {
320                 printf "\"%s\"", $_;
321             } else {
322                 print $_;
323             }
324         }
325     }
326     print "\n";
327 }
328
329 =head1 OVERVIEW
330
331 Virt-tools are a set of tools that you can install in your virtual
332 machines (host and guests) to get enhanced information about the
333 guests.
334
335 Unlike VMWare Tools, virt-tools is hypervisor agnostic.  Also
336 virt-tools is just about collecting statistics and does not include
337 any performance or functionality enhancements for guests (see virtio
338 if you want that).
339
340 There are two parts to any virt-tools installation: some client
341 programs like C<virt-uname> and C<virt-ps> that you run on the host,
342 to query guest information.  On the guest, you have to install and run
343 a virt-tools service.  Between the host and guest is a transport which
344 should be secured.
345
346 The L</GUEST ARCHITECTURE> section describes how virt-tools appears
347 from the guest side.
348
349 The L</HOST ARCHITECTURE> section describes the architecture of
350 virt-tools on the host side.
351
352 =head1 GUEST ARCHITECTURE
353
354 In most cases, you can just install the C<virt-tools-guest> package in
355 your Linux guests, or the Windows virt-tools guest package in your
356 Windows guests, and everything should just work.  In this section we
357 describe more about how it works (or is supposed to work) from the
358 guest side.
359
360 =head2 COMMUNICATIONS DIRECTORY
361
362 The guest writes various static, mostly unchanging, information into
363 its own directory.  On Linux the directory is
364 C<@localstatedir@/lib/virt-tools/> and under Windows it is
365 C<%systemroot%\virttool\>.  In the discussion below, this
366 communications directory is referred to as C<$GUESTCOMMSDIR>.
367
368 The host is able to read files out of this directory using
369 L<libguestfs(3)> (without any cooperation needed by the guest).
370
371 =head2 IP ADDRESSES
372
373 The host can't easily see the guest's IP address.  The host provides
374 the guest with a network interface connected to a bridge, but the
375 guest can use any IP address it likes (although well-behaved guests
376 will usually have some static IPs or are allocated one by DHCP).
377
378 So when the guest starts up, or its IP address changes (usually these
379 are rare events) the guest writes a file
380 C<$GUESTCOMMSDIR/ip-E<lt>ifaceE<gt>> which contains details of the IP
381 address of the interface E<lt>ifaceE<gt> (eg. the file might be called
382 C<ip-eth0> under Linux).
383
384 C<virt-ifconfig> reads this file directly using L<libguestfs(3)>.
385
386 =head2 KEYS
387
388 When the guest is first installed (or more precisely, when the
389 virt-tools-guest package is first installed in the guest), a random
390 secret key is generated.  This is used to encrypt communications with
391 the guest, and it is described in more detail below.
392
393 The key is written to C<$GUESTCOMMSDIR/key>.
394
395 =head2 SNMP DAEMON
396
397 For process listings, and just about every other piece of data except
398 for IP address, guests run a completely standard SNMP (Simple Network
399 Management Protocol) server.  The host client tools access this server
400 in order to query information about the guest.  They query this using
401 standard SNMP calls.
402
403 The protocol used is SNMPv3 (RFC 2571) which addresses security
404 concerns in earlier versions of the protocol.  In order to ensure that
405 only the host can access the SNMP server and see the results, all
406 communications are encrypted and authenticated using the guest's key.
407
408 =head2 TRANSPORT
409
410 There is not necessarily a network connection between the host and the
411 guest.  There are many configurations of virtualization in which the
412 host has no network access to the guest: for example, if the host
413 firewalls itself off from the guest (or vice versa), or if the guest
414 has a physically separate network card from the host.
415
416 Therefore the guest to host SNMP transport is not necessarily over an
417 IP network.  Other transports are possible, including "vmchannel"
418 (where "vmchannel" is the generic name for a collection of specialized
419 host-guest communication channels implemented in different ways by
420 different hypervisors).
421
422 The transport is written to C<$GUESTCOMMSDIR/transport>.
423
424 =head1 HOST ARCHITECTURE
425
426 On the host side, the host uses L<libguestfs(3)> to read the guest's
427 IP address and key, and uses some heuristics to determine the
428 transport to use.
429
430 Once the key and the transport to the guest are worked out, programs
431 like C<virt-ps>, C<virt-uname> and so on are just making
432 straightforward SNMP calls:
433
434  +-----------------+      +-----------------+
435  | host            |      | guest           |
436  |  virt-ps --- request ---> snmpd          |
437  |         <---- reply -----                |
438  +-----------------+      +-----------------+
439
440 The difficulty is in determining the key and the transport to use,
441 which is what this section covers.  You can also use this knowledge to
442 diagnose problems or to create non-standard configurations.
443
444 =head2 DETERMINE KEY
445
446 All the host tools use an external helper program called
447 C<virt-tools-get-key> to get the key of the guest.  (See
448 L<virt-tools-get-key(8)> for the precise usage of this program).
449
450 The key is generated by the guest once -- when the virt-tools-guest
451 package is installed in the guest.  The key is written to a file
452 C<$GUESTCOMMSDIR/key> (in the guest) which is readable only by root.
453
454 Using L<libguestfs(3)> the host can read any file in the guest, so it
455 can read this key out directly.  This is what the
456 C<virt-tools-get-key> program does, and you can run it by hand to
457 verify its operation:
458
459  # virt-tools-get-key -v domname
460  abcdef1234567890
461
462 =head3 KEY CACHE
463
464 C<virt-tools-get-key> caches the keys of guests that it has seen
465 before so it doesn't have to read them each time.  The cache is in
466 C<@localstatedir@/lib/virt-tools/keys/> (in the host).
467
468 You can just delete the files in this directory at any time, I<or> you
469 can drop a file in here which contains the key of a guest.
470
471 To do this, create a file
472 C<@localstatedir@/lib/virt-tools/keys/E<lt>UUIDE<gt>> where
473 E<lt>UUIDE<gt> is the guest's UUID as displayed by this command:
474
475  virsh domuuid <name>
476
477 The contents of the file should be the key.
478
479 You can test this works by running C<virt-tools-get-key> by hand.
480
481 This cache never expires, unless you remove the files by hand.
482
483 =cut
484
485 sub get_key
486 {
487     my $domname = shift;
488
489     my $cmd = "virt-tools-get-key";
490     $cmd .= " -v" if $verbose;
491     # XXX quoting
492     $cmd .= " -c '$uri'" if $uri;
493     $cmd .= " '$domname'";
494
495     print STDERR "$cmd\n" if $verbose;
496
497     open PIPE, "$cmd |" or die "$cmd: $!";
498     my $line = <PIPE>;
499     die __"no response from virt-tools-get-key\n" unless $line;
500     chomp $line;
501     close PIPE;
502
503     $line
504 }
505
506 =head2 DETERMINE TRANSPORT
507
508 All the host tools use a second helper program called
509 C<virt-tools-get-transport> to get the transport and address to use
510 for a guest.  (See L<virt-tools-get-transport(8)> for the precise
511 usage of this program).
512
513 This program tries a series of methods to determine how to access a
514 guest, be it through a direct network connection or over some
515 hypervisor-specific vmchannel.
516
517  # virt-tools-get-transport -v domname
518  udp:192.168.122.33
519
520 You can diagnose problems with the transport by trying to run this
521 command by hand.
522
523 =head3 TRANSPORT CACHE
524
525 C<virt-tools-get-transport> caches the transports of guests that it
526 has seen before so it doesn't have to determine them each time.  The
527 cache is in C<@localstatedir@/lib/virt-tools/transports/> (in the
528 host).
529
530 As for the L</KEY CACHE>, this directory is just some files that are
531 named after the UUID of the guest, containing the transport.
532
533 Unlike the key cache, C<virt-tools-get-transport> will check that a
534 transport is still valid, and will expire (ie. delete) the
535 corresponding entry in the transport cache if it is not valid.
536
537 =cut
538
539 sub get_transport
540 {
541     my $domname = shift;
542
543     my $cmd = "virt-tools-get-transport";
544     $cmd .= " -v" if $verbose;
545     # XXX quoting
546     $cmd .= " -c '$uri'" if $uri;
547     $cmd .= " '$domname'";
548
549     print STDERR "$cmd\n" if $verbose;
550
551     open PIPE, "$cmd |" or die "$cmd: $!";
552     my $line = <PIPE>;
553     die __"no response from virt-tools-get-transport\n" unless $line;
554     chomp $line;
555     close PIPE;
556
557     $line
558 }
559
560 =head2 SNMP QUERIES
561
562 Standard SNMP queries are used between the host and guest.
563
564 SNMP already supports many of the features we are trying to query
565 (eg. the UCD SNMP MIB provides a way to query the process list of a
566 machine in a form which is a de facto standard).
567
568 To determine what precise queries are sent, run the tools in verbose
569 mode or examine the source.
570
571 =cut
572
573 sub get_snmp_session
574 {
575     my $key = shift;
576     my $transport = shift;
577
578     my ($hostname, $port, $domain);
579     if ($transport =~ /^udp:(.*):(.*)/) {
580         $domain = "udp";
581         $hostname = $1;
582         $port = $2;
583     } elsif ($transport =~ /^tcp:(.*):(.*)/) {
584         $domain = "tcp";
585         $hostname = $1;
586         $port = $2;
587     } else {
588         die __x("unknown transport type: {t}", t => $transport);
589     }
590
591     if ($verbose) {
592         print STDERR "creating Net::SNMP session to $domain:$hostname:$port with key $key\n"
593     }
594
595     my ($session, $error) = Net::SNMP->session (
596         -version => 3,
597         -username => "virttools",
598         -authpassword => $key,
599         -authprotocol => "sha",
600         -privpassword => $key,
601         -privprotocol => "aes",
602         -hostname => $hostname,
603         -port => $port,
604         -domain => $domain,
605         );
606     die __x("SNMP failure: {e}", e => $error) unless $session;
607
608     $session;
609 }
610
611 =head2 RUNNING YOUR OWN SNMP SERVER IN A GUEST
612
613 I<(To be written)>
614
615 =head1 NOTE ABOUT CSV FORMAT
616
617 Comma-separated values (CSV) is a deceptive format.  It I<seems> like
618 it should be easy to parse, but it is definitely not easy to parse.
619
620 Myth: Just split fields at commas.  Reality: This does I<not> work
621 reliably.  This example has two columns:
622
623  "foo,bar",baz
624
625 Myth: Read the file one line at a time.  Reality: This does I<not>
626 work reliably.  This example has one row:
627
628  "foo
629  bar",baz
630
631 For shell scripts, use C<csvtool> (L<http://merjis.com/developers/csv>
632 also packaged in major Linux distributions).
633
634 For other languages, use a CSV processing library (eg. C<Text::CSV>
635 for Perl or Python's built-in csv library).
636
637 Most spreadsheets and databases can import CSV directly.
638
639 =head1 SEE ALSO
640
641 L<virt-ifconfig(8)>,
642 L<guestfs(3)>,
643 L<guestfish(1)>,
644 L<Sys::Guestfs(3)>,
645 L<Sys::Guestfs::Lib(3)>,
646 L<Sys::Virt(3)>,
647 L<http://libguestfs.org/>.
648
649 =head1 AUTHORS
650
651 =over 4
652
653 =item *
654
655 Richard W.M. Jones (C<rjones at redhat dot com>)
656
657 =item *
658
659 Matthew Booth (C<mbooth at redhat dot com>)
660
661 =back
662
663 =head1 COPYRIGHT
664
665 Copyright (C) 2009 Red Hat Inc.
666
667 This program is free software; you can redistribute it and/or modify
668 it under the terms of the GNU General Public License as published by
669 the Free Software Foundation; either version 2 of the License, or
670 (at your option) any later version.
671
672 This program is distributed in the hope that it will be useful,
673 but WITHOUT ANY WARRANTY; without even the implied warranty of
674 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
675 GNU General Public License for more details.
676
677 You should have received a copy of the GNU General Public License
678 along with this program; if not, write to the Free Software
679 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.