Implement virt-uptime command.
[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");
233 }
234
235 sub do_ping
236 {
237     my $domname = shift;
238     my $key = shift;
239     my $transport = shift;
240
241
242
243 }
244
245 =head1 virt-uname
246
247 C<virt-uname> displays the system information (kernel version etc) of
248 the guest.
249
250 =cut
251
252 sub title_uname
253 {
254     print_row (__"Guest", __"System name");
255 }
256
257 sub do_uname
258 {
259     my $domname = shift;
260     my $key = shift;
261     my $transport = shift;
262
263     my $session = get_snmp_session ($key, $transport);
264     my $sysDescr = "1.3.6.1.2.1.1.1.0";
265     my $r = $session->get_request (-varbindlist => [$sysDescr])
266         or die __x("SNMP error: {e}", $session->error);
267     print_row ($domname, $r->{$sysDescr});
268     $session->close;
269 }
270
271 =head1 virt-uptime
272
273 C<virt-uptime> displays the uptime of the guest
274
275 =cut
276
277 sub title_uptime
278 {
279     print_row (__"Guest", __"Uptime");
280 }
281
282 sub do_uptime
283 {
284     my $domname = shift;
285     my $key = shift;
286     my $transport = shift;
287
288     my $session = get_snmp_session ($key, $transport);
289     my $sysUpTime = "1.3.6.1.2.1.1.3.0";
290     my $r = $session->get_request (-varbindlist => [$sysUpTime])
291         or die __x("SNMP error: {e}", $session->error);
292     print_row ($domname, $r->{$sysUpTime});
293     $session->close;
294 }
295
296 sub print_row
297 {
298     my @fields = @_;
299
300     local $_;
301     my $comma = 0;
302
303     foreach (@fields) {
304         if (!$csv) {
305             printf "%-16s ", $_
306         } else {
307             print "," if $comma;
308             $comma = 1;
309
310             # XXX Use Text::CSV here.
311             if ($_ =~ /"/) {
312                 s/"/""/;
313                 printf "\"%s\"", $_;
314             } elsif ($_ =~ /,/ || $_ =~ /\n/) {
315                 printf "\"%s\"", $_;
316             } else {
317                 print $_;
318             }
319         }
320     }
321     print "\n";
322 }
323
324 =head1 OVERVIEW
325
326 Virt-tools are a set of tools that you can install in your virtual
327 machines (host and guests) to get enhanced information about the
328 guests.
329
330 Unlike VMWare Tools, virt-tools is hypervisor agnostic.  Also
331 virt-tools is just about collecting statistics and does not include
332 any performance or functionality enhancements for guests (see virtio
333 if you want that).
334
335 There are two parts to any virt-tools installation: some client
336 programs like C<virt-uname> and C<virt-ps> that you run on the host,
337 to query guest information.  On the guest, you have to install and run
338 a virt-tools service.  Between the host and guest is a transport which
339 should be secured.
340
341 The L</GUEST ARCHITECTURE> section describes how virt-tools appears
342 from the guest side.
343
344 The L</HOST ARCHITECTURE> section describes the architecture of
345 virt-tools on the host side.
346
347 =head1 GUEST ARCHITECTURE
348
349 In most cases, you can just install the C<virt-tools-guest> package in
350 your Linux guests, or the Windows virt-tools guest package in your
351 Windows guests, and everything should just work.  In this section we
352 describe more about how it works (or is supposed to work) from the
353 guest side.
354
355 =head2 COMMUNICATIONS DIRECTORY
356
357 The guest writes various static, mostly unchanging, information into
358 its own directory.  On Linux the directory is
359 C<@localstatedir@/lib/virt-tools/> and under Windows it is
360 C<%systemroot%\virttool\>.  In the discussion below, this
361 communications directory is referred to as C<$GUESTCOMMSDIR>.
362
363 The host is able to read files out of this directory using
364 L<libguestfs(3)> (without any cooperation needed by the guest).
365
366 =head2 IP ADDRESSES
367
368 The host can't easily see the guest's IP address.  The host provides
369 the guest with a network interface connected to a bridge, but the
370 guest can use any IP address it likes (although well-behaved guests
371 will usually have some static IPs or are allocated one by DHCP).
372
373 So when the guest starts up, or its IP address changes (usually these
374 are rare events) the guest writes a file
375 C<$GUESTCOMMSDIR/ip-E<lt>ifaceE<gt>> which contains details of the IP
376 address of the interface E<lt>ifaceE<gt> (eg. the file might be called
377 C<ip-eth0> under Linux).
378
379 C<virt-ifconfig> reads this file directly using L<libguestfs(3)>.
380
381 =head2 KEYS
382
383 When the guest is first installed (or more precisely, when the
384 virt-tools-guest package is first installed in the guest), a random
385 secret key is generated.  This is used to encrypt communications with
386 the guest, and it is described in more detail below.
387
388 The key is written to C<$GUESTCOMMSDIR/key>.
389
390 =head2 SNMP DAEMON
391
392 For process listings, and just about every other piece of data except
393 for IP address, guests run a completely standard SNMP (Simple Network
394 Management Protocol) server.  The host client tools access this server
395 in order to query information about the guest.  They query this using
396 standard SNMP calls.
397
398 The protocol used is SNMPv3 (RFC 2571) which addresses security
399 concerns in earlier versions of the protocol.  In order to ensure that
400 only the host can access the SNMP server and see the results, all
401 communications are encrypted and authenticated using the guest's key.
402
403 =head2 TRANSPORT
404
405 There is not necessarily a network connection between the host and the
406 guest.  There are many configurations of virtualization in which the
407 host has no network access to the guest: for example, if the host
408 firewalls itself off from the guest (or vice versa), or if the guest
409 has a physically separate network card from the host.
410
411 Therefore the guest to host SNMP transport is not necessarily over an
412 IP network.  Other transports are possible, including "vmchannel"
413 (where "vmchannel" is the generic name for a collection of specialized
414 host-guest communication channels implemented in different ways by
415 different hypervisors).
416
417 The transport is written to C<$GUESTCOMMSDIR/transport>.
418
419 =head1 HOST ARCHITECTURE
420
421 On the host side, the host uses L<libguestfs(3)> to read the guest's
422 IP address and key, and uses some heuristics to determine the
423 transport to use.
424
425 Once the key and the transport to the guest are worked out, programs
426 like C<virt-ps>, C<virt-uname> and so on are just making
427 straightforward SNMP calls:
428
429  +-----------------+      +-----------------+
430  | host            |      | guest           |
431  |  virt-ps --- request ---> snmpd          |
432  |         <---- reply -----                |
433  +-----------------+      +-----------------+
434
435 The difficulty is in determining the key and the transport to use,
436 which is what this section covers.  You can also use this knowledge to
437 diagnose problems or to create non-standard configurations.
438
439 =head2 DETERMINE KEY
440
441 All the host tools use an external helper program called
442 C<virt-tools-get-key> to get the key of the guest.  (See
443 L<virt-tools-get-key(8)> for the precise usage of this program).
444
445 The key is generated by the guest once -- when the virt-tools-guest
446 package is installed in the guest.  The key is written to a file
447 C<$GUESTCOMMSDIR/key> (in the guest) which is readable only by root.
448
449 Using L<libguestfs(3)> the host can read any file in the guest, so it
450 can read this key out directly.  This is what the
451 C<virt-tools-get-key> program does, and you can run it by hand to
452 verify its operation:
453
454  # virt-tools-get-key -v domname
455  abcdef1234567890
456
457 =head3 KEY CACHE
458
459 C<virt-tools-get-key> caches the keys of guests that it has seen
460 before so it doesn't have to read them each time.  The cache is in
461 C<@localstatedir@/lib/virt-tools/keys/> (in the host).
462
463 You can just delete the files in this directory at any time, I<or> you
464 can drop a file in here which contains the key of a guest.
465
466 To do this, create a file
467 C<@localstatedir@/lib/virt-tools/keys/E<lt>UUIDE<gt>> where
468 E<lt>UUIDE<gt> is the guest's UUID as displayed by this command:
469
470  virsh domuuid <name>
471
472 The contents of the file should be the key.
473
474 You can test this works by running C<virt-tools-get-key> by hand.
475
476 This cache never expires, unless you remove the files by hand.
477
478 =cut
479
480 sub get_key
481 {
482     my $domname = shift;
483
484     my $cmd = "virt-tools-get-key";
485     $cmd .= " -v" if $verbose;
486     # XXX quoting
487     $cmd .= " -c '$uri'" if $uri;
488     $cmd .= " '$domname'";
489
490     print STDERR "$cmd\n" if $verbose;
491
492     open PIPE, "$cmd |" or die "$cmd: $!";
493     my $line = <PIPE>;
494     die __"no response from virt-tools-get-key\n" unless $line;
495     chomp $line;
496     close PIPE;
497
498     $line
499 }
500
501 =head2 DETERMINE TRANSPORT
502
503 All the host tools use a second helper program called
504 C<virt-tools-get-transport> to get the transport and address to use
505 for a guest.  (See L<virt-tools-get-transport(8)> for the precise
506 usage of this program).
507
508 This program tries a series of methods to determine how to access a
509 guest, be it through a direct network connection or over some
510 hypervisor-specific vmchannel.
511
512  # virt-tools-get-transport -v domname
513  udp:192.168.122.33
514
515 You can diagnose problems with the transport by trying to run this
516 command by hand.
517
518 =head3 TRANSPORT CACHE
519
520 C<virt-tools-get-transport> caches the transports of guests that it
521 has seen before so it doesn't have to determine them each time.  The
522 cache is in C<@localstatedir@/lib/virt-tools/transports/> (in the
523 host).
524
525 As for the L</KEY CACHE>, this directory is just some files that are
526 named after the UUID of the guest, containing the transport.
527
528 Unlike the key cache, C<virt-tools-get-transport> will check that a
529 transport is still valid, and will expire (ie. delete) the
530 corresponding entry in the transport cache if it is not valid.
531
532 =cut
533
534 sub get_transport
535 {
536     my $domname = shift;
537
538     my $cmd = "virt-tools-get-transport";
539     $cmd .= " -v" if $verbose;
540     # XXX quoting
541     $cmd .= " -c '$uri'" if $uri;
542     $cmd .= " '$domname'";
543
544     print STDERR "$cmd\n" if $verbose;
545
546     open PIPE, "$cmd |" or die "$cmd: $!";
547     my $line = <PIPE>;
548     die __"no response from virt-tools-get-transport\n" unless $line;
549     chomp $line;
550     close PIPE;
551
552     $line
553 }
554
555 =head2 SNMP QUERIES
556
557 Standard SNMP queries are used between the host and guest.
558
559 SNMP already supports many of the features we are trying to query
560 (eg. the UCD SNMP MIB provides a way to query the process list of a
561 machine in a form which is a de facto standard).
562
563 To determine what precise queries are sent, run the tools in verbose
564 mode or examine the source.
565
566 =cut
567
568 sub get_snmp_session
569 {
570     my $key = shift;
571     my $transport = shift;
572
573     my ($hostname, $port, $domain);
574     if ($transport =~ /^udp:(.*):(.*)/) {
575         $domain = "udp";
576         $hostname = $1;
577         $port = $2;
578     } elsif ($transport =~ /^tcp:(.*):(.*)/) {
579         $domain = "tcp";
580         $hostname = $1;
581         $port = $2;
582     } else {
583         die __x("unknown transport type: {t}", t => $transport);
584     }
585
586     if ($verbose) {
587         print STDERR "creating Net::SNMP session to $domain:$hostname:$port with key $key\n"
588     }
589
590     my ($session, $error) = Net::SNMP->session (
591         -version => 3,
592         -username => "virttools",
593         -authpassword => $key,
594         -authprotocol => "sha",
595         -privpassword => $key,
596         -privprotocol => "aes",
597         -hostname => $hostname,
598         -port => $port,
599         -domain => $domain,
600         );
601     die __x("SNMP failure: {e}", e => $error) unless $session;
602
603     $session;
604 }
605
606 =head2 RUNNING YOUR OWN SNMP SERVER IN A GUEST
607
608 I<(To be written)>
609
610 =head1 NOTE ABOUT CSV FORMAT
611
612 Comma-separated values (CSV) is a deceptive format.  It I<seems> like
613 it should be easy to parse, but it is definitely not easy to parse.
614
615 Myth: Just split fields at commas.  Reality: This does I<not> work
616 reliably.  This example has two columns:
617
618  "foo,bar",baz
619
620 Myth: Read the file one line at a time.  Reality: This does I<not>
621 work reliably.  This example has one row:
622
623  "foo
624  bar",baz
625
626 For shell scripts, use C<csvtool> (L<http://merjis.com/developers/csv>
627 also packaged in major Linux distributions).
628
629 For other languages, use a CSV processing library (eg. C<Text::CSV>
630 for Perl or Python's built-in csv library).
631
632 Most spreadsheets and databases can import CSV directly.
633
634 =head1 SEE ALSO
635
636 L<virt-ifconfig(8)>,
637 L<guestfs(3)>,
638 L<guestfish(1)>,
639 L<Sys::Guestfs(3)>,
640 L<Sys::Guestfs::Lib(3)>,
641 L<Sys::Virt(3)>,
642 L<http://libguestfs.org/>.
643
644 =head1 AUTHORS
645
646 =over 4
647
648 =item *
649
650 Richard W.M. Jones (C<rjones at redhat dot com>)
651
652 =item *
653
654 Matthew Booth (C<mbooth at redhat dot com>)
655
656 =back
657
658 =head1 COPYRIGHT
659
660 Copyright (C) 2009 Red Hat Inc.
661
662 This program is free software; you can redistribute it and/or modify
663 it under the terms of the GNU General Public License as published by
664 the Free Software Foundation; either version 2 of the License, or
665 (at your option) any later version.
666
667 This program is distributed in the hope that it will be useful,
668 but WITHOUT ANY WARRANTY; without even the implied warranty of
669 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
670 GNU General Public License for more details.
671
672 You should have received a copy of the GNU General Public License
673 along with this program; if not, write to the Free Software
674 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.