Add libguestfs-test-tool.

This is an end-user testing tool, designed to test basic functionality
of libguestfs/qemu/kernel combination on the end-user's final host
machine.

It does not perform a thorough test, but should be enough to find
most booting issues.

Also this is intended to be used when reporting bugs.
This commit is contained in:
Richard Jones
2009-07-22 20:58:39 +01:00
parent 26f69f706e
commit f9de01b9b9
11 changed files with 714 additions and 1 deletions

3
.gitignore vendored
View File

@@ -169,4 +169,7 @@ src/guestfs-structs.h
src/.pod2text.data
src/stamp-generator
stamp-h1
test-tool/libguestfs-test-tool.1
test-tool/libguestfs-test-tool
test-tool/libguestfs-test-tool-helper
v2v/virt-v2v.1

View File

@@ -91,6 +91,9 @@ src/
Source code to the C library.
Also contains the crucial generator program.
test-tool/
Interactive qemu/kernel test tool.
v2v/
Xen to KVM (V2V) conversion tool.

View File

@@ -18,7 +18,7 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src daemon appliance fish po examples images \
capitests regressions
capitests regressions test-tool
if HAVE_OCAML
SUBDIRS += ocaml

View File

@@ -574,6 +574,7 @@ AC_CONFIG_FILES([Makefile
images/Makefile
capitests/Makefile
regressions/Makefile
test-tool/Makefile
ocaml/Makefile ocaml/examples/Makefile
perl/Makefile
python/Makefile

View File

@@ -982,6 +982,11 @@ That you are testing a recent version.
Describe the bug accurately, and give a way to reproduce it.
=item *
Run libguestfs-test-tool and paste the B<complete, unedited>
output into the bug report.
=back
=head1 AUTHORS

View File

@@ -74,4 +74,6 @@ ruby/ext/guestfs/_guestfs.c
src/guestfs-actions.c
src/guestfs-bindtests.c
src/guestfs.c
test-tool/helper.c
test-tool/test-tool.c
v2v/virt-v2v.pl

44
test-tool/Makefile.am Normal file
View File

@@ -0,0 +1,44 @@
# libguestfs
# Copyright (C) 2009 Red Hat Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
EXTRA_DIST = libguestfs-test-tool.pod
CLEANFILES =
bin_PROGRAMS = libguestfs-test-tool
libexec_PROGRAMS = libguestfs-test-tool-helper
man_MANS = libguestfs-test-tool.1
AM_CPPFLAGS = \
-DDEFAULT_HELPER='"$(libexecdir)/libguestfs-test-tool-helper"'
libguestfs_test_tool_SOURCES = test-tool.c
libguestfs_test_tool_CFLAGS = \
-I$(top_srcdir)/src -I$(top_builddir)/src \
-Wall
libguestfs_test_tool_LDADD = \
$(top_builddir)/src/libguestfs.la
libguestfs_test_tool_helper_SOURCES = helper.c
libguestfs_test_tool_helper_LDFLAGS = -all-static
libguestfs-test-tool.1: libguestfs-test-tool.pod
$(POD2MAN) \
--section 1 \
-c "Virtualization Support" \
--release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
$< > $@

9
test-tool/README Normal file
View File

@@ -0,0 +1,9 @@
This is a test tool, not a comprehensive test, but a "does it
basically function" test, which can be packaged and given to end
users.
It also collects and prints a lot of internal debug information, which
is useful in bug reports to track down appliance / qemu boot problems.
For more information, please read the man page
libguestfs-test-tool(1).

73
test-tool/helper.c Normal file
View File

@@ -0,0 +1,73 @@
/* libguestfs-test-tool-helper
* Copyright (C) 2009 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* NB. This program is intended to run inside the appliance. */
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
char buffer[10 * 1024];
int
main (void)
{
int fd;
fprintf (stderr, "This is the libguestfs-test-tool helper program.\n");
/* This should fail immediately if we're not in the appliance. */
if (mkdir ("/tmp", 0700) == -1) {
perror ("mkdir");
fprintf (stderr, "This program should not be run directly. Use libguestfs-test-tool instead.\n");
exit (1);
}
if (geteuid () != 0) {
fprintf (stderr, "helper: This program doesn't appear to be running as root.\n");
exit (1);
}
if (mkdir ("/tmp/helper", 0700) == -1) {
perror ("/tmp/helper");
exit (1);
}
fd = open ("/tmp/helper/a", O_CREAT|O_EXCL|O_WRONLY, 0600);
if (fd == -1) {
perror ("create /tmp/helper/a");
exit (1);
}
if (write (fd, buffer, sizeof buffer) != sizeof buffer) {
perror ("write");
exit (1);
}
if (close (fd) == -1) {
perror ("close");
exit (1);
}
exit (0);
}

View File

@@ -0,0 +1,136 @@
=encoding utf8
=head1 NAME
libguestfs-test-tool - End user tests for libguestfs
=head1 SYNOPSIS
libguestfs-test-tool [--options]
=head1 DESCRIPTION
libguestfs-test-tool is a test program shipped with libguestfs to end
users and developers, to allow them to check basic libguestfs
functionality is working. This is needed because libguestfs
occasionally breaks for reasons beyond our control: usually because of
changes in the underlying qemu or kernel packages, or the host
environment.
If you suspect a problem in libguestfs, then just run:
libguestfs-test-tool
It will print lots of diagnostic messages.
If it runs to completion successfully, you will see this near the end:
===== TEST FINISHED OK =====
and the test tool will exit with code 0.
If it fails (and/or exits with non-zero error code), please paste the
B<complete, unedited> output of the test tool into a bug report. More
information about reporting bugs can be found on the
L<http://libguestfs.org/> website.
=head1 OPTIONS
=over 4
=item I<--help>
Display short usage information and exit.
=item I<--helper /path/to/libguestfs-test-tool-helper>
Pass an alternate name for the helper program. libguestfs-test-tool
will normally look in the C<$libexec> directory that was configured
when the tool was built.
=item I<--qemu qemu_binary>
If you have downloaded another qemu binary, point this option at the
full path of the binary to try it.
=item I<--qemudir qemu_source_dir>
If you have compiled qemu from source, point this option at the source
directory to try it.
=item I<--timeout N>
Set the launch timeout to C<N> seconds. The default is 120 seconds
which does not usually need to be adjusted unless your machine is very
slow.
=back
=head1 TRYING OUT A DIFFERENT VERSION OF QEMU
If you have compiled another version of qemu from source and would
like to try that, then you can use the I<--qemudir> option to point to
the qemu source directory.
If you have downloaded a qemu binary from somewhere, use the I<--qemu>
option to point to the binary.
When using an alternate qemu with libguestfs, usually you would need
to write a qemu wrapper script (see section I<QEMU WRAPPERS> in
L<guestfs(3)>). libguestfs-test-tool writes a temporary qemu wrapper
script when you use either of the I<--qemudir> or I<--qemu> options.
=head1 EXIT CODE
libguestfs-test-tool returns I<0> if the tests completed without
error, or I<1> if there was an error.
=head1 FILES
=over 4
=item /usr/libexec/libguestfs-test-tool-helper
This helper program is run inside the appliance and provides
additional tests.
=item /usr/bin/mkisofs
The C<mkisofs> command is required in order to construct a CD-ROM ISO
file which is used as part of the tests.
=back
=head1 ENVIRONMENT VARIABLES
For the full list of environment variables which may affect
libguestfs, please see the L<guestfs(3)> manual page.
=head1 SEE ALSO
L<guestfs(3)>,
L<http://libguestfs.org/>,
L<http://qemu.org/>.
=head1 AUTHORS
Richard W.M. Jones (C<rjones at redhat dot com>)
=head1 COPYRIGHT
Copyright (C) 2009 Red Hat Inc.
L<http://libguestfs.org/>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

437
test-tool/test-tool.c Normal file
View File

@@ -0,0 +1,437 @@
/* libguestfs-test-tool
* Copyright (C) 2009 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <guestfs.h>
#ifdef HAVE_GETTEXT
#include "gettext.h"
#define _(str) dgettext(PACKAGE, (str))
#define N_(str) dgettext(PACKAGE, (str))
#else
#define _(str) str
#define N_(str) str
#endif
#define DEFAULT_TIMEOUT 120
static const char *helper = DEFAULT_HELPER;
static int timeout = DEFAULT_TIMEOUT;
static char tmpf[] = "/tmp/libguestfs-test-tool-sda-XXXXXX";
static char isof[] = "/tmp/libguestfs-test-tool-iso-XXXXXX";
static guestfs_h *g;
static void preruncheck (void);
static void make_files (void);
static void set_qemu (const char *path, int use_wrapper);
static void
usage (void)
{
printf (_("libguestfs-test-tool: interactive test tool\n"
"Copyright (C) 2009 Red Hat Inc.\n"
"Usage:\n"
" libguestfs-test-tool [--options]\n"
"Options:\n"
" --help Display usage\n"
" --helper libguestfs-test-tool-helper\n"
" Helper program (default: %s)\n"
" --qemudir dir Specify QEMU source directory\n"
" --qemu qemu Specify QEMU binary\n"
" --timeout n\n"
" -t n Set launch timeout (default: %d seconds)\n"
),
DEFAULT_HELPER, DEFAULT_TIMEOUT);
}
int
main (int argc, char *argv[])
{
static const char *options = "?";
static struct option long_options[] = {
{ "help", 0, 0, '?' },
{ "helper", 1, 0, 0 },
{ "qemu", 1, 0, 0 },
{ "qemudir", 1, 0, 0 },
{ "timeout", 1, 0, 't' },
{ 0, 0, 0, 0 }
};
int c;
int option_index;
extern char **environ;
int i;
struct guestfs_version *vers;
char *sfdisk_lines[] = { ",", NULL };
char *str;
/* XXX This is wrong if the user renames the helper. */
char *helper_args[] = { "/iso/libguestfs-test-tool-helper", NULL };
for (;;) {
c = getopt_long (argc, argv, options, long_options, &option_index);
if (c == -1) break;
switch (c) {
case 0: /* options which are long only */
if (strcmp (long_options[option_index].name, "helper") == 0)
helper = optarg;
else if (strcmp (long_options[option_index].name, "qemu") == 0)
set_qemu (optarg, 0);
else if (strcmp (long_options[option_index].name, "qemudir") == 0)
set_qemu (optarg, 1);
else {
fprintf (stderr,
_("libguestfs-test-tool: unknown long option: %s (%d)\n"),
long_options[option_index].name, option_index);
exit (1);
}
break;
case 't':
if (sscanf (optarg, "%d", &timeout) != 1 || timeout < 0) {
fprintf (stderr,
_("libguestfs-test-tool: invalid timeout: %s\n"),
optarg);
exit (1);
}
break;
case '?':
usage ();
exit (0);
default:
fprintf (stderr,
_("libguestfs-test-tool: unexpected command line option 0x%x\n"),
c);
exit (1);
}
}
preruncheck ();
make_files ();
printf ("===== Test starts here =====\n");
/* Must set LIBGUESTFS_DEBUG=1 */
setenv ("LIBGUESTFS_DEBUG", "1", 1);
/* Print out any environment variables which may relate to this test. */
for (i = 0; environ[i] != NULL; ++i)
if (strncmp (environ[i], "LIBGUESTFS_", 11) == 0)
printf ("%s\n", environ[i]);
/* Create the handle and configure it. */
g = guestfs_create ();
if (g == NULL) {
fprintf (stderr,
_("libguestfs-test-tool: failed to create libguestfs handle\n"));
exit (1);
}
if (guestfs_add_drive (g, tmpf) == -1) {
fprintf (stderr,
_("libguestfs-test-tool: failed to add drive '%s'\n"),
tmpf);
exit (1);
}
if (guestfs_add_drive (g, isof) == -1) {
fprintf (stderr,
_("libguestfs-test-tool: failed to add drive '%s'\n"),
isof);
exit (1);
}
/* Print any version info etc. */
vers = guestfs_version (g);
if (vers == NULL) {
fprintf (stderr, _("libguestfs-test-tool: guestfs_version failed\n"));
exit (1);
}
printf ("library version: %"PRIi64".%"PRIi64".%"PRIi64"%s\n",
vers->major, vers->minor, vers->release, vers->extra);
guestfs_free_version (vers);
printf ("guestfs_get_append: %s\n", guestfs_get_append (g) ? : "(null)");
printf ("guestfs_get_autosync: %d\n", guestfs_get_autosync (g));
printf ("guestfs_get_memsize: %d\n", guestfs_get_memsize (g));
printf ("guestfs_get_path: %s\n", guestfs_get_path (g));
printf ("guestfs_get_qemu: %s\n", guestfs_get_qemu (g));
printf ("guestfs_get_verbose: %d\n", guestfs_get_verbose (g));
/* Launch the guest handle. */
if (guestfs_launch (g) == -1) {
fprintf (stderr,
_("libguestfs-test-tool: failed to launch appliance\n"));
exit (1);
}
printf ("Launching appliance, timeout set to %d seconds.\n", timeout);
fflush (stdout);
alarm (timeout);
if (guestfs_wait_ready (g) == -1) {
fprintf (stderr,
_("libguestfs-test-tool: failed or timed out in 'wait_ready'\n"));
exit (1);
}
alarm (0);
printf ("Guest launched OK.\n");
fflush (stdout);
/* Create the filesystem and mount everything. */
if (guestfs_sfdiskM (g, "/dev/sda", sfdisk_lines) == -1) {
fprintf (stderr,
_("libguestfs-test-tool: failed to run sfdisk\n"));
exit (1);
}
if (guestfs_mkfs (g, "ext2", "/dev/sda1") == -1) {
fprintf (stderr,
_("libguestfs-test-tool: failed to mkfs.ext2\n"));
exit (1);
}
if (guestfs_mount (g, "/dev/sda1", "/") == -1) {
fprintf (stderr,
_("libguestfs-test-tool: failed to mount /dev/sda1 on /\n"));
exit (1);
}
if (guestfs_mkdir (g, "/iso") == -1) {
fprintf (stderr,
_("libguestfs-test-tool: failed to mkdir /iso\n"));
exit (1);
}
if (guestfs_mount (g, "/dev/sdb", "/iso") == -1) {
fprintf (stderr,
_("libguestfs-test-tool: failed to mount /dev/sdb on /iso\n"));
exit (1);
}
/* Let's now run some simple tests using the helper program. */
str = guestfs_command (g, helper_args);
if (str == NULL) {
fprintf (stderr,
_("libguestfs-test-tool: could not run helper program, or helper failed\n"));
exit (1);
}
free (str);
printf ("===== TEST FINISHED OK =====\n");
exit (0);
}
static char qemuwrapper[] = "/tmp/libguestfs-test-tool-wrapper-XXXXXX";
static void
cleanup_wrapper (void)
{
unlink (qemuwrapper);
}
/* Handle the --qemu and --qemudir parameters. use_wrapper is true
* in the --qemudir (source directory) case, where we have to create
* a wrapper shell script.
*/
static void
set_qemu (const char *path, int use_wrapper)
{
char buffer[PATH_MAX];
struct stat statbuf;
int fd;
FILE *fp;
if (getenv ("LIBGUESTFS_QEMU")) {
fprintf (stderr,
_("LIBGUESTFS_QEMU environment variable is already set, so\n"
"--qemu/--qemudir options cannot be used.\n"));
exit (1);
}
if (!use_wrapper) {
if (access (path, X_OK) == -1) {
fprintf (stderr,
_("Binary '%s' does not exist or is not executable\n"),
path);
exit (1);
}
setenv ("LIBGUESTFS_QEMU", path, 1);
return;
}
/* This should be a source directory, so check it. */
snprintf (buffer, sizeof buffer, "%s/pc-bios", path);
if (stat (buffer, &statbuf) == -1 ||
!S_ISDIR (statbuf.st_mode)) {
fprintf (stderr,
_("%s: does not look like a qemu source directory\n"),
path);
exit (1);
}
/* Make a wrapper script. */
fd = mkstemp (qemuwrapper);
if (fd == -1) {
perror (qemuwrapper);
exit (1);
}
fchmod (fd, 0700);
fp = fdopen (fd, "w");
fprintf (fp,
"#!/bin/sh -\n"
"qemudir='%s'\n"
"\"$qemudir\"/",
path);
/* Select the right qemu binary for the wrapper script. */
#ifdef __i386__
fprintf (fp, "i386-softmmu/qemu");
#else
fprintf (fp, host_cpu "-softmmu/qemu-system-" host_cpu);
#endif
fprintf (fp, " -L \"$qemudir\"/pc-bios \"$@\"\n");
fclose (fp);
setenv ("LIBGUESTFS_QEMU", qemuwrapper, 1);
atexit (cleanup_wrapper);
}
/* After getting the command line args, but before running
* anything, we check everything is in place to do the tests.
*/
static void
preruncheck (void)
{
int r;
FILE *fp;
char cmd[256];
char buffer[1024];
if (access (helper, R_OK) == -1) {
fprintf (stderr,
_("Test tool helper program 'libguestfs-test-tool-helper' is not\n"
"available. Expected to find it in '%s'\n"
"\n"
"Use the --helper option to specify the location of this program.\n"),
helper);
exit (1);
}
snprintf (cmd, sizeof cmd, "file '%s'", helper);
fp = popen (cmd, "r");
if (fp == NULL) {
perror (cmd);
exit (1);
}
r = fread (buffer, 1, sizeof buffer - 1, fp);
if (r == 0) {
fprintf (stderr, _("command failed: %s"), cmd);
exit (1);
}
pclose (fp);
buffer[r] = '\0';
if (strstr (buffer, "statically linked") == NULL) {
fprintf (stderr,
_("Test tool helper program %s\n"
"is not statically linked. This is a build error when this test tool\n"
"was built.\n"),
helper);
exit (1);
}
}
static void
cleanup_tmpfiles (void)
{
unlink (tmpf);
unlink (isof);
}
static void
make_files (void)
{
int fd, r;
char cmd[256];
/* Make the ISO which will contain the helper program. */
fd = mkstemp (isof);
if (fd == -1) {
perror (isof);
exit (1);
}
close (fd);
snprintf (cmd, sizeof cmd, "mkisofs -quiet -rJT -o '%s' '%s'",
isof, helper);
r = system (cmd);
if (r == -1 || WEXITSTATUS(r) != 0) {
fprintf (stderr,
_("mkisofs command failed: %s\n"), cmd);
exit (1);
}
/* Allocate the sparse file for /dev/sda. */
fd = mkstemp (tmpf);
if (fd == -1) {
perror (tmpf);
unlink (isof);
exit (1);
}
if (lseek (fd, 100 * 1024 * 1024 - 1, SEEK_SET) == -1) {
perror ("lseek");
close (fd);
unlink (tmpf);
unlink (isof);
exit (1);
}
if (write (fd, "\0", 1) == -1) {
perror ("write");
close (fd);
unlink (tmpf);
unlink (isof);
exit (1);
}
close (fd);
atexit (cleanup_tmpfiles); /* Removes tmpf and isof. */
}