From: "Richard W.M. Jones" <rjones(a)redhat.com>
---
.gitignore | 9 +-
Makefile.am | 7 +-
clone/Makefile.am | 51 ----
clone/test-virt-sysprep.sh | 49 ----
clone/virt-sysprep.in | 408 --------------------------
clone/virt-sysprep.pod | 521 ---------------------------------
configure.ac | 4 +-
contrib/make-check-on-installed.pl | 2 +-
src/guestfs.pod | 9 +-
sysprep/Makefile.am | 126 ++++++++
sysprep/main.ml | 232 +++++++++++++++
sysprep/sysprep_operation.ml | 164 +++++++++++
sysprep/sysprep_operation.mli | 96 ++++++
sysprep/sysprep_operation_hostname.ml | 69 +++++
sysprep/sysprep_operation_utmp.ml | 43 +++
sysprep/test-virt-sysprep.sh | 26 ++
sysprep/utils.ml | 69 +++++
sysprep/virt-sysprep.pod | 419 ++++++++++++++++++++++++++
tests/extra/Makefile.am | 4 +-
19 files changed, 1258 insertions(+), 1050 deletions(-)
delete mode 100644 clone/Makefile.am
delete mode 100755 clone/test-virt-sysprep.sh
delete mode 100644 clone/virt-sysprep.in
delete mode 100755 clone/virt-sysprep.pod
create mode 100644 sysprep/Makefile.am
create mode 100644 sysprep/main.ml
create mode 100644 sysprep/sysprep_operation.ml
create mode 100644 sysprep/sysprep_operation.mli
create mode 100644 sysprep/sysprep_operation_hostname.ml
create mode 100644 sysprep/sysprep_operation_utmp.ml
create mode 100755 sysprep/test-virt-sysprep.sh
create mode 100644 sysprep/utils.ml
create mode 100755 sysprep/virt-sysprep.pod
diff --git a/.gitignore b/.gitignore
index 14d5c75..337ff75 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,9 +26,6 @@ cat/virt-ls
cat/virt-ls.1
ChangeLog
*.class
-clone/stamp-virt-sysprep.pod
-clone/virt-sysprep
-clone/virt-sysprep.1
*.cma
*.cmi
*.cmo
@@ -345,6 +342,12 @@ src/libguestfs.syms
src/.libs/libguestfs.so
src/stamp-guestfs.pod
stamp-h1
+sysprep/.depend
+sysprep/stamp-virt-sysprep.pod
+sysprep/sysprep-extra-options.pod
+sysprep/sysprep-operations.pod
+sysprep/virt-sysprep
+sysprep/virt-sysprep.1
*.swp
test1.img
test2.img
diff --git a/Makefile.am b/Makefile.am
index de2d4ca..5f104fa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -91,7 +91,7 @@ SUBDIRS += csharp
# virt-resize (new version) and virt-sparsify are written in OCaml.
if HAVE_OCAML
-SUBDIRS += resize sparsify
+SUBDIRS += resize sparsify sysprep
endif
# Perl tools.
@@ -104,11 +104,6 @@ if HAVE_FUSE
SUBDIRS += fuse
endif
-# virt-tools in shell. This uses guestmount and virt-inspector.
-if HAVE_FUSE
-SUBDIRS += clone
-endif
-
# po-docs must come after tools, inspector.
if HAVE_PO4A
SUBDIRS += po-docs
diff --git a/clone/Makefile.am b/clone/Makefile.am
deleted file mode 100644
index 4d586c6..0000000
--- a/clone/Makefile.am
+++ /dev/null
@@ -1,51 +0,0 @@
-# libguestfs cloning tools
-# Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-include $(top_srcdir)/subdir-rules.mk
-
-EXTRA_DIST = \
- test-virt-sysprep.sh \
- virt-sysprep.pod
-
-CLEANFILES = stamp-virt-sysprep.pod
-
-bin_SCRIPTS = virt-sysprep
-
-# Manual pages and HTML files for the website.
-man_MANS = virt-sysprep.1
-noinst_DATA = $(top_builddir)/html/virt-sysprep.1.html
-
-virt-sysprep.1 $(top_builddir)/html/virt-sysprep.1.html: stamp-virt-sysprep.pod
-
-stamp-virt-sysprep.pod: virt-sysprep.pod
- $(top_builddir)/podwrapper.sh \
- --man virt-sysprep.1 \
- --html $(top_builddir)/html/virt-sysprep.1.html \
- $<
- touch $@
-
-# Tests.
-
-random_val := $(shell awk 'BEGIN{srand(); print 1+int(255*rand())}' <
/dev/null)
-
-TESTS_ENVIRONMENT = \
- MALLOC_PERTURB_=$(random_val) \
- $(top_builddir)/run
-
-if ENABLE_APPLIANCE
-TESTS = test-virt-sysprep.sh
-endif ENABLE_APPLIANCE
diff --git a/clone/test-virt-sysprep.sh b/clone/test-virt-sysprep.sh
deleted file mode 100755
index 3217389..0000000
--- a/clone/test-virt-sysprep.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/bash -
-# libguestfs virt-sysprep test script
-# Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-export LANG=C
-set -e
-
-if [ ! -w /dev/fuse ]; then
- echo "SKIPPING virt-sysprep test, because there is no /dev/fuse."
- exit 0
-fi
-
-rm -f test.img guestfish
-
-qemu-img create -f qcow2 -o backing_file=../tests/guests/fedora.img test.img
-
-# Provide alternate 'virt-inspector' and 'guestmount' binaries
-# that run the just-built programs.
-
-cat <<'EOF' > virt-inspector
-#!/bin/sh -
-../run ../inspector/virt-inspector "$@"
-EOF
-chmod +x virt-inspector
-cat <<'EOF' > guestmount
-#!/bin/sh -
-../run ../fuse/guestmount "$@"
-EOF
-chmod +x guestmount
-
-PATH=.:$PATH
-
-./virt-sysprep -a test.img
-
-rm -f test.img virt-inspector guestmount
diff --git a/clone/virt-sysprep.in b/clone/virt-sysprep.in
deleted file mode 100644
index d505532..0000000
--- a/clone/virt-sysprep.in
+++ /dev/null
@@ -1,408 +0,0 @@
-#!/bin/bash -
-# @configure_input@
-# libguestfs virt-sysprep tool
-# Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-unset CDPATH
-program="virt-sysprep"
-version="@PACKAGE_VERSION@"
-
-# Uncomment this to see every shell command that is executed.
-#set -x
-
-TEMP=`getopt \
- -o a:c:d:vVx \
- --long
help,add:,connect:,domain:,enable:,format::,hostname:,list-operations,selinux-relabel,no-selinux-relabel,verbose,version
\
- -n $program -- "$@"`
-if [ $? != 0 ]; then
- echo "$program: problem parsing the command line arguments"
- exit 1
-fi
-eval set -- "$TEMP"
-
-# This array accumulates the arguments we pass through to guestmount.
-declare -a params
-i=0
-
-verbose=
-add_params=0
-enable=
-hostname_param=localhost.localdomain
-selinux_relabel=auto
-
-usage ()
-{
- echo "Usage:"
- echo " $program [--options] -d domname"
- echo " $program [--options] -a disk.img [-a disk.img ...]"
- echo
- echo "Read $program(1) man page for more information."
- echo
- echo "NOTE: $program modifies the guest or disk image *in place*."
- exit $1
-}
-
-while true; do
- case "$1" in
- -a|--add)
- params[i++]="-a"
- params[i++]="$2"
- ((add_params++))
- shift 2;;
- -c|--connect)
- params[i++]="-c"
- params[i++]="$2"
- shift 2;;
- -d|--domain)
- params[i++]="-d"
- params[i++]="$2"
- ((add_params++))
- shift 2;;
- --enable)
- if [ -n "$enable" ]; then
- echo "error: --enable option can only be given once"
- exit 1
- fi
- enable="$2"
- shift 2;;
- --format)
- if [ -n "$2" ]; then
- params[i++]="--format=$2"
- else
- params[i++]="--format"
- fi
- shift 2;;
- --help)
- usage 0;;
- --hostname)
- hostname_param="$2"
- shift 2;;
- --list-operations)
- enable=list
- shift;;
- --selinux-relabel)
- selinux_relabel=yes
- shift;;
- --no-selinux-relabel)
- selinux_relabel=no
- shift;;
- -v|--verbose)
- params[i++]="-v"
- verbose=yes
- shift;;
- -V|--version)
- echo "$program $version"
- exit 0;;
- -x)
- # Can't pass the -x option directly to guestmount because
- # that stops guestmount from forking, which means we can't
- # coordinate with guestmount when it has finished
- # initializing. So instead set just the underlying option
- # in libguestfs by exporting LIBGUESTFS_TRACE.
- # Unfortunately (a) this omits FUSE calls, but don't worry
- # about that for now, and more importantly (b) trace
- # messages disappear into never-never land after the fork.
- export LIBGUESTFS_TRACE=1
- shift;;
- --)
- shift
- break;;
- *)
- echo "Internal error!"
- exit 1;;
- esac
-done
-
-# Different sysprep operations that can be enabled. Default is to
-# enable all of these, although some of them are only done on certain
-# guest types (see details below).
-if [ -z "$enable" ]; then
- cron_spool=yes
- dhcp_client_state=yes
- dhcp_server_state=yes
- hostname=yes
- logfiles=yes
- mail_spool=yes
- net_hwaddr=yes
- random_seed=yes
- rhn_systemid=yes
- smolt_uuid=yes
- ssh_hostkeys=yes
- udev_persistent_net=yes
- utmp=yes
- yum_uuid=yes
-elif [ "$enable" = "list" ]; then
- echo "cron-spool"
- echo "dhcp-client-state"
- echo "dhcp-server-state"
- echo "hostname"
- echo "logfiles"
- echo "mail-spool"
- echo "net-hwaddr"
- echo "random-seed"
- echo "rhn-systemid"
- echo "smolt-uuid"
- echo "ssh-hostkeys"
- echo "udev-persistent-net"
- echo "utmp"
- echo "yum-uuid"
- exit 0
-else
- for opt in $(echo "$enable" | sed 's/,/ /g'); do
- case "$opt" in
- cron-spool) cron_spool=yes ;;
- dhcp-client-state) dhcp_client_state=yes ;;
- dhcp-server-state) dhcp_server_state=yes ;;
- hostname) hostname=yes ;;
- logfiles) logfiles=yes ;;
- mail-spool) mail_spool=yes ;;
- net-hwaddr) net_hwaddr=yes ;;
- random-seed) random_seed=yes ;;
- rhn-systemid) rhn_systemid=yes ;;
- smolt-uuid) smolt_uuid=yes ;;
- ssh-hostkeys) ssh_hostkeys=yes ;;
- udev-persistent-net) udev_persistent_net=yes ;;
- utmp) utmp=yes ;;
- yum-uuid) yum_uuid=yes ;;
- *)
- echo "error: unknown --enable feature: $opt"
- exit 1
- esac
- done
-fi
-
-# Make sure there were no extra parameters on the command line.
-if [ $# -gt 0 ]; then
- echo "error: $program: extra parameters on the command line"
- echo
- usage 1
-fi
-
-# Did the user specify at least one -a or -d option?
-if [ $add_params -eq 0 ]; then
- echo "error: $program: you need at least one -a or -d option"
- echo
- usage 1
-fi
-
-# end of command line parsing
-#----------------------------------------------------------------------
-
-set -e
-
-if [ "$verbose" = "yes" ]; then
- echo params: "${params[@]}"
-fi
-
-# Create a temporary directory for general purpose use during operations.
-tmpdir="$(mktemp -d)"
-
-cleanup ()
-{
- if [ -d $tmpdir/mnt ]; then
- fusermount -u $tmpdir/mnt >/dev/null 2>&1 ||:
- fi
- rm -rf $tmpdir ||:
-}
-trap cleanup EXIT ERR
-
-# Run virt-inspector and grab inspection information about this guest.
-virt-inspector "${params[@]}" > $tmpdir/xml
-virt-inspector --xpath \
- "string(/operatingsystems/operatingsystem[position()=1]/name)" \
- < $tmpdir/xml > $tmpdir/type
-virt-inspector --xpath \
- "string(/operatingsystems/operatingsystem[position()=1]/distro)" \
- < $tmpdir/xml > $tmpdir/distro ||:
-virt-inspector --xpath \
- "string(/operatingsystems/operatingsystem[position()=1]/package_format)" \
- < $tmpdir/xml > $tmpdir/package_format ||:
-virt-inspector --xpath \
-
"string(/operatingsystems/operatingsystem[position()=1]/package_management)" \
- < $tmpdir/xml > $tmpdir/package_management ||:
-
-type="$(cat $tmpdir/type)"
-distro="$(cat $tmpdir/distro)"
-package_format="$(cat $tmpdir/package_format)"
-package_management="$(cat $tmpdir/package_management)"
-
-# Mount the disk.
-mkdir $tmpdir/mnt
-guestmount --rw -i "${params[@]}" $tmpdir/mnt
-
-mnt="$tmpdir/mnt"
-
-#----------------------------------------------------------------------
-# The sysprep operations.
-
-if [ "$cron_spool" = "yes" ]; then
- rm -rf $mnt/var/spool/cron/*
-fi
-
-if [ "$dhcp_client_state" = "yes" ]; then
- case "$type" in
- linux)
- rm -rf $mnt/var/lib/dhclient/*
- # RHEL 3:
- rm -rf $mnt/var/lib/dhcp/*
- ;;
- esac
-fi
-
-if [ "$dhcp_server_state" = "yes" ]; then
- case "$type" in
- linux)
- rm -rf $mnt/var/lib/dhcpd/*
- ;;
- esac
-fi
-
-if [ "$hostname" = "yes" ]; then
- case "$type/$distro" in
- linux/fedora|linux/rhel)
- echo "HOSTNAME=$hostname_param" >
$mnt/etc/sysconfig/network.new
- sed '/^HOSTNAME=/d' < $mnt/etc/sysconfig/network >>
$mnt/etc/sysconfig/network.new
- mv -f $mnt/etc/sysconfig/network.new $mnt/etc/sysconfig/network
- created_files=yes
- ;;
- linux/debian|linux/ubuntu)
- echo "$hostname_param" > $mnt/etc/hostname
- created_files=yes
- ;;
- esac
-fi
-
-if [ "$logfiles" = "yes" ]; then
- case "$type" in
- linux)
- rm -rf $mnt/var/log/*.log*
- rm -rf $mnt/var/log/audit/*
- rm -rf $mnt/var/log/btmp*
- rm -rf $mnt/var/log/cron*
- rm -rf $mnt/var/log/dmesg*
- rm -rf $mnt/var/log/lastlog*
- rm -rf $mnt/var/log/maillog*
- rm -rf $mnt/var/log/mail/*
- rm -rf $mnt/var/log/messages*
- rm -rf $mnt/var/log/secure*
- rm -rf $mnt/var/log/spooler*
- rm -rf $mnt/var/log/tallylog*
- rm -rf $mnt/var/log/wtmp*
- ;;
- esac
-fi
-
-if [ "$mail_spool" = "yes" ]; then
- rm -rf $mnt/var/spool/mail/*
- rm -rf $mnt/var/mail/*
-fi
-
-if [ "$net_hwaddr" = "yes" ]; then
- case "$type/$distro" in
- linux/fedora|linux/rhel)
- if [ -d $mnt/etc/sysconfig/network-scripts ]; then
- rm_hwaddr ()
- {
- sed '/^HWADDR=/d' < "$1" >
"$1.new"
- mv -f "$1.new" "$1"
- }
- export -f rm_hwaddr
- find $mnt/etc/sysconfig/network-scripts \
- -name 'ifcfg-*' -type f \
- -exec bash -c 'rm_hwaddr "$0"' {} \;
- created_files=yes
- fi
- ;;
- esac
-fi
-
-if [ "$random_seed" = "yes" -a "$type" = "linux"
]; then
- f=
- if [ -f $mnt/var/lib/random-seed ]; then
- # Fedora
- f=$mnt/var/lib/random-seed
- elif [ -f $mnt/var/lib/urandom/random-seed ]; then
- # Debian
- f=$mnt/var/lib/urandom/random-seed
- fi
- if [ -n "$f" ]; then
- dd if=/dev/urandom of="$f" bs=8 count=1 conv=nocreat,notrunc
2>/dev/null
- fi
-fi
-
-if [ "$rhn_systemid" = "yes" -a "$type/$distro" =
"linux/rhel" ]; then
- rm -f $mnt/etc/sysconfig/rhn/systemid
-fi
-
-if [ "$smolt_uuid" = "yes" -a "$type" = "linux"
]; then
- rm -f $mnt/etc/sysconfig/hw-uuid
- rm -f $mnt/etc/smolt/uuid
- rm -f $mnt/etc/smolt/hw-uuid
-fi
-
-if [ "$ssh_hostkeys" = "yes" -a "$type" !=
"windows" ]; then
- rm -rf $mnt/etc/ssh/*_host_*
-fi
-
-if [ "$udev_persistent_net" = "yes" -a "$type" =
"linux" ]; then
- rm -f $mnt/etc/udev/rules.d/70-persistent-net.rules
-fi
-
-if [ "$utmp" = "yes" -a "$type" != "windows" ];
then
- rm -f $mnt/var/run/utmp
-fi
-
-if [ "$yum_uuid" = "yes" -a "$package_management" =
"yum" ]; then
- rm -f $mnt/var/lib/yum/uuid
-fi
-
-#----------------------------------------------------------------------
-# Clean up and close down.
-
-# If we created any new files and the guest uses SELinux, then we have
-# to relabel the filesystem on boot. Could do with a better way to
-# test "guest uses SELinux" (XXX).
-case "$selinux_relabel/$created_files" in
- yes/*)
- touch $mnt/.autorelabel;;
- auto/yes)
- case "$type/$distro" in
-
linux/fedora|linux/rhel|linux/centos|linux/scientificlinux|linux/redhat-based)
- touch $mnt/.autorelabel
- ;;
- esac
- ;;
-esac
-
-sync
-
-# Unfortunately various unwanted processes jump into mountpoints.
-# tracker-miner-fs is the latest, previously it was something called
-# gvfs-gdu-volume-monitor. Therefore if the mountpoint is busy, try
-# to unmount it a few times. XXX
-count=10
-while ! fusermount -u $tmpdir/mnt && [ $count -gt 0 ]; do
- sleep 1
- ((count--))
-done
-if [ $count -eq 0 ]; then exit 1; fi
-
-rm -rf $tmpdir
-
-trap - EXIT ERR
-
-exit 0
diff --git a/clone/virt-sysprep.pod b/clone/virt-sysprep.pod
deleted file mode 100755
index 5cab3eb..0000000
--- a/clone/virt-sysprep.pod
+++ /dev/null
@@ -1,521 +0,0 @@
-=encoding utf8
-
-=head1 NAME
-
-virt-sysprep - Reset or unconfigure a virtual machine so clones can be made
-
-=head1 SYNOPSIS
-
- virt-sysprep [--options] -d domname
-
- virt-sysprep [--options] -a disk.img [-a disk.img ...]
-
-=head1 DESCRIPTION
-
-Virt-sysprep "resets" or "unconfigures" a virtual machine so that
-clones can be made from it. Steps in this process include removing
-SSH host keys, removing persistent network MAC configuration, and
-removing user accounts. Each step can be enabled or disabled as
-required.
-
-Virt-sysprep is a simple shell script, allowing easy inspection or
-customization by the system administrator.
-
-Virt-sysprep modifies the guest or disk image I<in place>. The guest
-must be shut down. If you want to preserve the existing contents of
-the guest, you I<must copy or clone the disk first>.
-See L</COPYING AND CLONING> below.
-
-You do I<not> need to run virt-sysprep as root. In fact we'd
-generally recommend that you don't. The time you might want to run it
-as root is when you need root in order to access the disk image, but
-even in this case it would be better to change the permissions on the
-disk image to be writable as the non-root user running virt-sysprep.
-
-"Sysprep" stands for "system preparation" tool. The name comes from
-the Microsoft program C<sysprep.exe> which is used to unconfigure
-Windows machines in preparation for cloning them. Having said that,
-virt-sysprep does I<not> currently work on Microsoft Windows guests.
-We plan to support Windows sysprepping in a future version, and we
-already have code to do it.
-
-=head1 OPTIONS
-
-=over 4
-
-=item B<--help>
-
-Display brief help.
-
-=item B<-a> file
-
-=item B<--add> file
-
-Add I<file> which should be a disk image from a virtual machine.
-
-The format of the disk image is auto-detected. To override this and
-force a particular format use the I<--format=..> option.
-
-=item B<-c> URI
-
-=item B<--connect> URI
-
-If using libvirt, connect to the given I<URI>. If omitted, then we
-connect to the default libvirt hypervisor.
-
-If you specify guest block devices directly (I<-a>), then libvirt is
-not used at all.
-
-=item B<-d> guest
-
-=item B<--domain> guest
-
-Add all the disks from the named libvirt guest. Domain UUIDs can be
-used instead of names.
-
-=item B<--enable=...>
-
-Choose which sysprep operations to perform. Give a comma-separated
-list of operations, for example:
-
- --enable=ssh-hostkeys,udev-persistent-net
-
-would enable ONLY C<ssh-hostkeys> and C<udev-persistent-net> operations.
-
-If the I<--enable> option is not given, then we default to trying all
-possible sysprep operations. But some sysprep operations are skipped
-for some guest types.
-
-Use I<--list-operations> to list operations supported by a particular
-version of virt-sysprep.
-
-See L</OPERATIONS> below for a list and an explanation of each
-operation.
-
-=item B<--format=raw|qcow2|..>
-
-=item B<--format>
-
-The default for the I<-a> option is to auto-detect the format of the
-disk image. Using this forces the disk format for I<-a> options which
-follow on the command line. Using I<--format> with no argument
-switches back to auto-detection for subsequent I<-a> options.
-
-For example:
-
- virt-sysprep --format=raw -a disk.img
-
-forces raw format (no auto-detection) for C<disk.img>.
-
- virt-sysprep --format=raw -a disk.img --format -a another.img
-
-forces raw format (no auto-detection) for C<disk.img> and reverts to
-auto-detection for C<another.img>.
-
-If you have untrusted raw-format guest disk images, you should use
-this option to specify the disk format. This avoids a possible
-security problem with malicious guests (CVE-2010-3851).
-
-=item B<--hostname> newhostname
-
-Change the hostname. See the L</hostname> operation below.
-If not given, defaults to C<localhost.localdomain>.
-
-=item B<--list-operations>
-
-List the operations supported by the virt-sysprep program.
-
-=item B<--selinux-relabel>
-
-=item B<--no-selinux-relabel>
-
-I<--selinux-relabel> forces SELinux relabelling next time the guest
-boots. I<--no-selinux-relabel> disables relabelling.
-
-The default is to try to detect if SELinux relabelling is required.
-See L</SELINUX RELABELLING> below for more details.
-
-=item B<-v>
-
-=item B<--verbose>
-
-Enable verbose messages for debugging.
-
-=item B<-V>
-
-=item B<--version>
-
-Display version number and exit.
-
-=item B<-x>
-
-Enable tracing of libguestfs API calls.
-
-=back
-
-=head1 OPERATIONS
-
-If the I<--enable> option is I<not> given, then
-I<all sysprep operations are enabled>, although some are skipped
-depending on the type of guest.
-
-Operations can be individually enabled using the I<--enable> option.
-Use a comma-separated list, for example:
-
- virt-sysprep --enable=ssh-hostkeys,udev-persistent-net [etc..]
-
-To list the operations supported by the current version of
-virt-sysprep, use I<--list-operations>.
-
-Future versions of virt-sysprep may add more operations. If you are
-using virt-sysprep and want predictable behaviour, specify only the
-operations that you want to have enabled.
-
-=head2 cron-spool
-
-Remove user at-jobs and cron-jobs.
-
-=head2 dhcp-client-state
-
-Remove DHCP client leases.
-
-=head2 dhcp-server-state
-
-Remove DHCP server leases.
-
-=head2 hostname
-
-Changes the hostname of the guest to the value given in the
-I<--hostname> parameter.
-
-If the I<--hostname> parameter is not given, then the hostname is
-changed to C<localhost.localdomain>.
-
-=head2 logfiles
-
-Remove many log files.
-
-=head2 mail-spool
-
-Remove email from the local mail spool directory.
-
-=head2 net-hwaddr
-
-Remove HWADDR (hard-coded MAC address) configuration. For Fedora and
-Red Hat Enterprise Linux, this is removed from C<ifcfg-*> files.
-
-=head2 random-seed
-
-Write some random bytes from the host into the random seed file of
-the guest.
-
-See L</RANDOM SEED> below.
-
-=head2 rhn-systemid
-
-Remove the RHN system ID.
-
-=head2 smolt-uuid
-
-Remove the Smolt hardware UUID.
-
-=head2 ssh-hostkeys
-
-Remove the SSH host keys in the guest.
-
-The SSH host keys are regenerated (differently) next time the guest is
-booted.
-
-If, after cloning, the guest gets the same IP address, ssh will give
-you a stark warning about the host key changing:
-
- @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
- @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
- @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
- IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
-
-=head2 udev-persistent-net
-
-Remove udev persistent net rules which map the guest's existing MAC
-address to a fixed ethernet device (eg. eth0).
-
-After a guest is cloned, the MAC address usually changes. Since the
-old MAC address occupies the old name (eg. eth0), this means the fresh
-MAC address is assigned to a new name (eg. eth1) and this is usually
-undesirable. Erasing the udev persistent net rules avoids this.
-
-=head2 utmp
-
-Remove the utmp file.
-
-This records who is currently logged in on a machine. In modern Linux
-distros it is stored in a ramdisk and hence not part of the virtual
-machine's disk, but it was stored on disk in older distros.
-
-=head2 yum-uuid
-
-Remove the yum UUID.
-
-Yum creates a fresh UUID the next time it runs when it notices that
-the original UUID has been erased.
-
-=head1 COPYING AND CLONING
-
-Virt-sysprep can be used as part of a process of cloning guests, or to
-prepare a template from which guests can be cloned. There are many
-different ways to achieve this using the virt tools, and this section
-is just an introduction.
-
-A virtual machine (when switched off) consists of two parts:
-
-=over 4
-
-=item I<configuration>
-
-The configuration or description of the guest. eg. The libvirt
-XML (see C<virsh dumpxml>), the running configuration of the guest,
-or another external format like OVF.
-
-Some configuration items that might need to be changed:
-
-=over 4
-
-=item *
-
-name
-
-=item *
-
-UUID
-
-=item *
-
-path to block device(s)
-
-=item *
-
-network card MAC address
-
-=back
-
-=item I<block device(s)>
-
-One or more hard disk images, themselves containing files,
-directories, applications, kernels, configuration, etc.
-
-Some things inside the block devices that might need to be changed:
-
-=over 4
-
-=item *
-
-hostname and other net configuration
-
-=item *
-
-UUID
-
-=item *
-
-SSH host keys
-
-=item *
-
-Windows unique security ID (SID)
-
-=item *
-
-Puppet registration
-
-=back
-
-=back
-
-=head2 COPYING THE BLOCK DEVICE
-
-Starting with an original guest, you probably wish to copy the guest
-block device and its configuration to make a template. Then once you
-are happy with the template, you will want to make many clones from
-it.
-
- virt-sysprep
- |
- v
- original guest --------> template ---------->
- \------> cloned
- \-----> guests
- \---->
-
-You can, of course, just copy the block device on the host using
-L<cp(1)> or L<dd(1)>.
-
- dd dd
- original guest --------> template ---------->
- \------> cloned
- \-----> guests
- \---->
-
-There are some smarter (and faster) ways too:
-
-=over 4
-
-=item *
-
- snapshot
- template ---------->
- \------> cloned
- \-----> guests
- \---->
-
-Use the block device as a backing file and create a snapshot on top
-for each guest. The advantage is that you don't need to copy the
-block device (very fast) and only changes are stored (less storage
-required).
-
-Note that writing to the backing file once you have created guests on
-top of it is not possible: you will corrupt the guests.
-
-Tools that can do this include:
-L<qemu-img(1)> (with the I<create -f qcow2 -o backing_file> option),
-L<lvcreate(8)> (I<--snapshot> option). Some filesystems (such as
-btrfs) and most Network Attached Storage devices can also create cheap
-snapshots from files or LUNs.
-
-=item *
-
-Get your NAS to snapshot and/or duplicate the LUN.
-
-=item *
-
-Prepare your template using L<virt-sparsify(1)>. See below.
-
-=back
-
-=head2 VIRT-CLONE
-
-A separate tool, L<virt-clone(1)>, can be used to duplicate the block
-device and/or modify the external libvirt configuration of a guest.
-It will reset the name, UUID and MAC address of the guest in the
-libvirt XML.
-
-L<virt-clone(1)> does not use libguestfs and cannot look inside the
-disk image. This was the original motivation to write virt-sysprep.
-
-=head2 SPARSIFY
-
- virt-sparsify
- original guest --------> template
-
-L<virt-sparsify(1)> can be used to make the cloning template smaller,
-making it easier to compress and/or faster to copy.
-
-Notice that since virt-sparsify also copies the image, you can use it
-to make the initial copy (instead of C<dd>).
-
-=head2 RESIZE
-
- virt-resize
- template ---------->
- \------> cloned
- \-----> guests
- \---->
-
-If you want to give people cloned guests, but let them pick the size
-of the guest themselves (eg. depending on how much they are prepared
-to pay for disk space), then instead of copying the template, you can
-run L<virt-resize(1)>. Virt-resize performs a copy and resize, and
-thus is ideal for cloning guests from a template.
-
-=head1 SECURITY
-
-Although virt-sysprep removes some sensitive information from the
-guest, it does not pretend to remove all of it. You should examine
-the L</OPERATIONS> above, and the implementation of the operations in
-the shell script. You should also examine the guest afterwards.
-
-Sensitive files are simply removed. The data they contained may still
-exist on the disk, easily recovered with a hex editor or undelete
-tool. Use L<virt-sparsify(1)> as one way to remove this content. See
-also the L<scrub(1)> command to get rid of deleted content in
-directory entries and inodes.
-
-=head2 RANDOM SEED
-
-I<(This section applies to Linux guests only)>
-
-The virt-sysprep C<random-seed> operation writes a few bytes of
-randomness from the host into the guest's random seed file.
-
-If this is just done once and the guest is cloned from the same
-template, then each guest will start with the same entropy, and things
-like SSH host keys and TCP sequence numbers may be predictable.
-
-Therefore you should arrange to add more randomness I<after> cloning
-from a template too, which can be done by just enabling the
-C<random-seed> operation:
-
- cp template.img newguest.img
- virt-sysprep --enable=random-seed -a newguest.img
-
-=head2 SELINUX RELABELLING
-
-I<(This section applies to Linux guests using SELinux only)>
-
-If any new files are created by virt-sysprep, then virt-sysprep
-touches C</.autorelabel> so that these will be correctly labelled by
-SELinux the next time the guest is booted. This process interrupts
-boot and can take some time.
-
-You can force relabelling for all guests by supplying the
-I<--selinux-relabel> option.
-
-You can disable relabelling entirely by supplying the
-I<--no-selinux-relabel> option.
-
-=head1 SHELL QUOTING
-
-Libvirt guest names can contain arbitrary characters, some of which
-have meaning to the shell such as C<#> and space. You may need to
-quote or escape these characters on the command line. See the shell
-manual page L<sh(1)> for details.
-
-=head1 EXIT STATUS
-
-This program returns 0 on success, or 1 if there was an error.
-
-=head1 SEE ALSO
-
-L<guestfs(3)>,
-L<guestfish(1)>,
-L<virt-clone(1)>,
-L<virt-rescue(1)>,
-L<virt-resize(1)>,
-L<virt-sparsify(1)>,
-L<virsh(1)>,
-L<lvcreate(8)>,
-L<qemu-img(1)>,
-L<scrub(1)>,
-L<http://libguestfs.org/>,
-L<http://libvirt.org/>.
-
-=head1 AUTHOR
-
-Richard W.M. Jones
L<http://people.redhat.com/~rjones/>
-
-=head1 COPYRIGHT
-
-Copyright (C) 2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
diff --git a/configure.ac b/configure.ac
index b8cc803..6d34460 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1149,8 +1149,6 @@ AC_CONFIG_HEADERS([config.h])
dnl
http://www.mail-archive.com/automake@gnu.org/msg10204.html
AC_CONFIG_FILES([appliance/libguestfs-make-fixed-appliance],
[chmod +x appliance/libguestfs-make-fixed-appliance])
-AC_CONFIG_FILES([clone/virt-sysprep],
- [chmod +x clone/virt-sysprep])
AC_CONFIG_FILES([podwrapper.sh],
[chmod +x podwrapper.sh])
AC_CONFIG_FILES([run],
@@ -1159,7 +1157,6 @@ AC_CONFIG_FILES([Makefile
align/Makefile
appliance/Makefile
cat/Makefile
- clone/Makefile
csharp/Makefile
daemon/Makefile
df/Makefile
@@ -1201,6 +1198,7 @@ AC_CONFIG_FILES([Makefile
ruby/examples/Makefile
sparsify/Makefile
src/Makefile
+ sysprep/Makefile
test-tool/Makefile
tests/c-api/Makefile
tests/data/Makefile
diff --git a/contrib/make-check-on-installed.pl b/contrib/make-check-on-installed.pl
index dc12dc5..83c408d 100755
--- a/contrib/make-check-on-installed.pl
+++ b/contrib/make-check-on-installed.pl
@@ -80,7 +80,7 @@ my %mapping = (
'/bin/virt-rescue$' => "rescue",
'/bin/virt-resize$' => "resize",
'/bin/virt-sparsify$' => "sparsify",
- '/bin/virt-sysprep$' => "clone",
+ '/bin/virt-sysprep$' => "sysprep",
'/bin/virt-tar$' => "tools",
'/bin/virt-tar-in$' => "fish",
'/bin/virt-tar-out$' => "fish",
diff --git a/src/guestfs.pod b/src/guestfs.pod
index 05f5c74..e2377b8 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -3008,11 +3008,6 @@ The libguestfs appliance, build scripts and so on.
The L<virt-cat(1)>, L<virt-filesystems(1)> and L<virt-ls(1)> commands
and documentation.
-=item C<clone>
-
-Tools for cloning virtual machines. Currently contains
-L<virt-sysprep(1)> command and documentation.
-
=item C<contrib>
Outside contributions, experimental parts.
@@ -3091,6 +3086,10 @@ L<virt-sparsify(1)> command and documentation.
Source code to the C library.
+=item C<sysprep>
+
+L<virt-sysprep(1)> command and documentation.
+
=item C<test-tool>
Test tool for end users to test if their qemu/kernel combination
diff --git a/sysprep/Makefile.am b/sysprep/Makefile.am
new file mode 100644
index 0000000..1675071
--- /dev/null
+++ b/sysprep/Makefile.am
@@ -0,0 +1,126 @@
+# libguestfs virt-sysprep tool
+# Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+include $(top_srcdir)/subdir-rules.mk
+
+SOURCES =
+
+EXTRA_DIST = \
+ $(SOURCES) \
+ test-virt-sysprep.sh \
+ virt-sysprep.pod
+
+CLEANFILES = stamp-virt-sysprep.pod
+
+if HAVE_OCAML
+
+# Alphabetical order.
+SOURCES += \
+ main.ml \
+ sysprep_operation.ml \
+ sysprep_operation.mli \
+ sysprep_operation_hostname.ml \
+ sysprep_operation_utmp.ml \
+ utils.ml
+
+# Note this list must be in dependency order.
+OBJECTS = \
+ utils.cmx \
+ sysprep_operation.cmx \
+ sysprep_operation_hostname.cmx \
+ sysprep_operation_utmp.cmx \
+ main.cmx
+
+bin_SCRIPTS = virt-sysprep
+
+# -I $(top_builddir)/src/.libs is a hack which forces corresponding -L
+# option to be passed to gcc, so we don't try linking against an
+# installed copy of libguestfs.
+OCAMLPACKAGES = -package unix -I $(top_builddir)/src/.libs -I $(top_builddir)/ocaml
+
+OCAMLCFLAGS = -g -warn-error CDEFLMPSUVYZX $(OCAMLPACKAGES)
+OCAMLOPTFLAGS = $(OCAMLCFLAGS)
+
+virt-sysprep: $(OBJECTS)
+ $(OCAMLFIND) ocamlopt $(OCAMLOPTFLAGS) \
+ mlguestfs.cmxa -linkpkg $^ -cclib -lncurses -o $@
+
+.mli.cmi:
+ $(OCAMLFIND) ocamlc $(OCAMLCFLAGS) -c $< -o $@
+.ml.cmo:
+ $(OCAMLFIND) ocamlc $(OCAMLCFLAGS) -c $< -o $@
+.ml.cmx:
+ $(OCAMLFIND) ocamlopt $(OCAMLCFLAGS) -c $< -o $@
+
+# Manual pages and HTML files for the website.
+man_MANS = virt-sysprep.1
+noinst_DATA = $(top_builddir)/html/virt-sysprep.1.html
+
+virt-sysprep.1 $(top_builddir)/html/virt-sysprep.1.html: stamp-virt-sysprep.pod
+
+stamp-virt-sysprep.pod: virt-sysprep.pod sysprep-extra-options.pod
sysprep-operations.pod
+ $(top_builddir)/podwrapper.sh \
+ --man virt-sysprep.1 \
+ --insert sysprep-extra-options.pod:@EXTRA_OPTIONS@ \
+ --insert sysprep-operations.pod:@OPERATIONS@ \
+ --html $(top_builddir)/html/virt-sysprep.1.html \
+ $<
+ touch $@
+
+sysprep-extra-options.pod: virt-sysprep
+ rm -f $@ $@-t
+ ./$< --dump-pod-options > $@-t
+ mv $@-t $@
+
+sysprep-operations.pod: virt-sysprep
+ rm -f $@ $@-t
+ ./$< --dump-pod > $@-t
+ mv $@-t $@
+
+# Tests.
+
+random_val := $(shell awk 'BEGIN{srand(); print 1+int(255*rand())}' <
/dev/null)
+
+TESTS_ENVIRONMENT = \
+ MALLOC_PERTURB_=$(random_val) \
+ $(top_builddir)/run
+
+if ENABLE_APPLIANCE
+TESTS = test-virt-sysprep.sh
+endif ENABLE_APPLIANCE
+
+# Dependencies.
+depend: .depend
+
+.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml)
+ rm -f $@ $@-t
+ $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) $^ | \
+ $(SED) 's/ *$$//' | \
+ $(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \
+ $(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \
+ sort > $@-t
+ mv $@-t $@
+
+-include .depend
+
+endif
+
+.PHONY: depend docs
+
+# Parallel builds don't obey dependencies for some reason we
+# don't understand.
+.NOTPARALLEL:
diff --git a/sysprep/main.ml b/sysprep/main.ml
new file mode 100644
index 0000000..67ef047
--- /dev/null
+++ b/sysprep/main.ml
@@ -0,0 +1,232 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Unix
+open Printf
+
+open Utils
+
+module G = Guestfs
+
+(* Finalize the list of operations modules. *)
+let () = Sysprep_operation.bake ()
+
+(* Command line argument parsing. *)
+let prog = Filename.basename Sys.executable_name
+
+let debug_gc, operations, g, selinux_relabel =
+ let debug_gc = ref false in
+ let domain = ref None in
+ let dryrun = ref false in
+ let files = ref [] in
+ let format = ref "auto" in
+ let libvirturi = ref "" in
+ let operations = ref None in
+ let selinux_relabel = ref `Auto in
+ let trace = ref false in
+ let verbose = ref false in
+
+ let display_version () =
+ let g = new G.guestfs () in
+ let version = g#version () in
+ printf "virt-sysprep %Ld.%Ld.%Ld%s\n"
+ version.G.major version.G.minor version.G.release version.G.extra;
+ exit 0
+ and add_file file =
+ let format = match !format with "auto" -> None | fmt -> Some fmt in
+ files := (file, format) :: !files
+ and set_domain dom =
+ if !domain <> None then (
+ eprintf "%s: --domain option can only be given once\n" prog;
+ exit 1
+ );
+ domain := Some dom
+ and dump_pod () =
+ Sysprep_operation.dump_pod ();
+ exit 0
+ and dump_pod_options () =
+ Sysprep_operation.dump_pod_options ();
+ exit 0
+ and set_enable ops =
+ if !operations <> None then (
+ eprintf "%s: --enable option can only be given once\n" prog;
+ exit 1
+ );
+ if ops = "" then (
+ eprintf "%s: you cannot pass an empty argument to --enable\n" prog;
+ exit 1
+ );
+ let ops = string_split "," ops in
+ let opset = List.fold_left (
+ fun opset op_name ->
+ try Sysprep_operation.add_to_set op_name opset
+ with Not_found ->
+ eprintf "%s: --enable: '%s' is not a known operation\n" prog
op_name;
+ exit 1
+ ) Sysprep_operation.empty_set ops in
+ operations := Some opset
+ and force_selinux_relabel () =
+ selinux_relabel := `Force
+ and no_force_selinux_relabel () =
+ selinux_relabel := `Never
+ and list_operations () =
+ Sysprep_operation.list_operations ();
+ exit 0
+ in
+
+ let argspec = Arg.align [
+ "-a", Arg.String add_file, "file Add disk image
file";
+ "--add", Arg.String add_file, "file Add disk image
file";
+ "-c", Arg.Set_string libvirturi, "uri Set libvirt URI";
+ "--connect", Arg.Set_string libvirturi, "uri Set libvirt URI";
+ "--debug-gc", Arg.Set debug_gc, " Debug GC and memory
allocations (internal)";
+ "-d", Arg.String set_domain, "domain Set libvirt guest
name";
+ "--domain", Arg.String set_domain, "domain Set libvirt guest
name";
+ "-n", Arg.Set dryrun, " Perform a dry run";
+ "--dryrun", Arg.Set dryrun, " Perform a dry run";
+ "--dry-run", Arg.Set dryrun, " Perform a dry run";
+ "--dump-pod", Arg.Unit dump_pod, " Dump POD (internal)";
+ "--dump-pod-options", Arg.Unit dump_pod_options, " Dump POD for
options (internal)";
+ "--enable", Arg.String set_enable, "operations Enable specific
operations";
+ "--format", Arg.Set_string format, "format Set format (default:
auto)";
+ "--list-operations", Arg.Unit list_operations, " List supported
operations";
+ "--selinux-relabel", Arg.Unit force_selinux_relabel, " Force SELinux
relabel";
+ "--no-selinux-relabel", Arg.Unit no_force_selinux_relabel, " Never do
SELinux relabel";
+ "-v", Arg.Set verbose, " Enable debugging
messages";
+ "--verbose", Arg.Set verbose, " -\"-";
+ "-V", Arg.Unit display_version, " Display version and
exit";
+ "--version", Arg.Unit display_version, " -\"-";
+ "-x", Arg.Set trace, " Enable tracing of libguestfs
calls";
+ ] in
+ let anon_fun _ = raise (Arg.Bad "extra parameter on the command line") in
+ let usage_msg =
+ sprintf "\
+%s: reset or unconfigure a virtual machine so clones can be made
+
+ virt-sysprep [--options] -d domname
+
+ virt-sysprep [--options] -a disk.img [-a disk.img ...]
+
+A short summary of the options is given below. For detailed help please
+read the man page virt-sysprep(1).
+"
+ prog in
+ Arg.parse argspec anon_fun usage_msg;
+
+ (* Check -a and -d options. *)
+ let files = !files in
+ let domain = !domain in
+ let libvirturi = match !libvirturi with "" -> None | s -> Some s in
+ let add =
+ match files, domain with
+ | [], None ->
+ eprintf "%s: you must give either -a or -d options\n" prog;
+ eprintf "Read virt-sysprep(1) man page for further information.\n";
+ exit 1
+ | [], Some dom ->
+ fun (g : Guestfs.guestfs) readonly ->
+ let allowuuid = true in
+ let readonlydisk = "ignore" (* ignore CDs, data drives *) in
+ ignore (g#add_domain ~readonly ?libvirturi ~allowuuid ~readonlydisk dom)
+ | _, Some _ ->
+ eprintf "%s: you cannot give -a and -d options together\n" prog;
+ eprintf "Read virt-sysprep(1) man page for further information.\n";
+ exit 1
+ | files, None ->
+ fun g readonly ->
+ List.iter (
+ fun (file, format) ->
+ g#add_drive_opts ~readonly ?format file
+ ) files
+ in
+
+ (* Dereference the rest of the args. *)
+ let debug_gc = !debug_gc in
+ let dryrun = !dryrun in
+ let operations = !operations in
+ let selinux_relabel = !selinux_relabel in
+ let trace = !trace in
+ let verbose = !verbose in
+
+ (* Connect to libguestfs. *)
+ let g = new G.guestfs () in
+ if trace then g#set_trace true;
+ if verbose then g#set_verbose true;
+ add g dryrun;
+ g#launch ();
+
+ debug_gc, operations, g, selinux_relabel
+
+let () =
+ (* Inspection. *)
+ match Array.to_list (g#inspect_os ()) with
+ | [] ->
+ eprintf "%s: no operating systems were found in the guest image\n" prog;
+ exit 1
+ | roots ->
+ List.iter (
+ fun root ->
+ (* Mount up the disks, like guestfish -i.
+ * See [ocaml/examples/inspect_vm.ml].
+ *)
+ let mps = g#inspect_get_mountpoints root in
+ let cmp (a,_) (b,_) = compare (String.length a) (String.length b) in
+ let mps = List.sort cmp mps in
+ List.iter (
+ fun (mp, dev) ->
+ try g#mount dev mp
+ with Guestfs.Error msg -> eprintf "%s (ignored)\n" msg
+ ) mps;
+
+ (* Perform the operations. *)
+ let flags = Sysprep_operation.perform_operations ?operations g root in
+
+ (* Parse flags. *)
+ let relabel = ref false in
+ List.iter (function
+ | `Created_files -> relabel := true
+ ) flags;
+
+ (* SELinux relabel? *)
+ let relabel =
+ match selinux_relabel, !relabel with
+ | `Force, _ -> true
+ | `Never, _ -> false
+ | `Auto, relabel -> relabel in
+ if relabel then (
+ let typ = g#inspect_get_type root in
+ let distro = g#inspect_get_distro root in
+ match typ, distro with
+ | "linux",
("fedora"|"rhel"|"redhat-based"
+ |"centos"|"scientificlinux") ->
+ g#touch "/.autorelabel"
+ | _ -> ()
+ );
+
+ (* Unmount everything in this guest. *)
+ g#umount_all ()
+ ) roots
+
+(* Finished. *)
+let () =
+ g#close ();
+
+ if debug_gc then
+ Gc.compact ();
+
+ exit 0
diff --git a/sysprep/sysprep_operation.ml b/sysprep/sysprep_operation.ml
new file mode 100644
index 0000000..8c8bd48
--- /dev/null
+++ b/sysprep/sysprep_operation.ml
@@ -0,0 +1,164 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+type flag = [ `Created_files ]
+
+type operation = {
+ name : string;
+ pod_description : string;
+ extra_args : ((Arg.key * Arg.spec * Arg.doc) * string) list;
+ perform : Guestfs.guestfs -> string -> flag list;
+}
+
+let ops = ref []
+
+let register_operation op = ops := op :: !ops
+
+let baked = ref false
+let rec bake () =
+ let ops' = List.sort (fun { name = a } { name = b } -> compare a b) !ops in
+ List.iter check ops';
+ ops := ops';
+ baked := true
+and check op =
+ let n = String.length op.name in
+ if n = 0 then (
+ eprintf "virt-sysprep: operation name is an empty string\n";
+ exit 1;
+ );
+ for i = 0 to n-1 do
+ match String.unsafe_get op.name i with
+ | 'a'..'z' | 'A'..'Z' | '0'..'9' |
'-' -> ()
+ | c ->
+ eprintf "virt-sysprep: disallowed character (%c) in operation name\n" c;
+ exit 1
+ done;
+ let n = String.length op.pod_description in
+ if n = 0 then (
+ eprintf "virt-sysprep: operation %s has no POD\n" op.name;
+ exit 1
+ );
+ if op.pod_description.[n-1] = '\n' then (
+ eprintf "virt-sysprep: POD for %s must not end with newline\n" op.name;
+ exit 1
+ )
+
+(* These internal functions are used to generate the man page. *)
+let dump_pod () =
+ assert !baked;
+
+ List.iter (
+ fun op ->
+ printf "=head2 B<%s>\n" op.name;
+ printf "\n";
+ printf "%s\n\n" op.pod_description
+ ) !ops
+
+(* Skip any leading '-' characters when comparing command line args. *)
+let skip_dashes str =
+ let n = String.length str in
+ let rec loop i =
+ if i >= n then assert false
+ else if str.[i] = '-' then loop (i+1)
+ else i
+ in
+ let i = loop 0 in
+ if i = 0 then str
+ else String.sub str i (n-i)
+
+let dump_pod_options () =
+ assert !baked;
+
+ let args = List.map (
+ fun { name = op_name; extra_args = extra_args } ->
+ List.map (fun ea -> op_name, ea) extra_args
+ ) !ops in
+ let args = List.flatten args in
+ let args = List.map (
+ fun (op_name, ((arg_name, spec, _), pod)) ->
+ match spec with
+ | Arg.Unit _
+ | Arg.Bool _
+ | Arg.Set _
+ | Arg.Clear _ ->
+ let heading = sprintf "B<%s>" arg_name in
+ arg_name, (op_name, heading, pod)
+ | Arg.String _
+ | Arg.Set_string _
+ |
Arg.Int _
+ | Arg.Set_int _
+ | Arg.Float _
+ | Arg.Set_float _ ->
+ let heading = sprintf "B<%s> %s" arg_name (skip_dashes arg_name)
in
+ arg_name, (op_name, heading, pod)
+ | Arg.Tuple _
+ | Arg.Symbol _
+ | Arg.Rest _ -> assert false (* XXX not implemented *)
+ ) args in
+
+ let args = List.sort (
+ fun (a, _) (b, _) ->
+ compare (skip_dashes a) (skip_dashes b)
+ ) args in
+
+ List.iter (
+ fun (arg_name, (op_name, heading, pod)) ->
+ printf "=item %s\n" heading;
+ printf "(see C<%s> below)\n" op_name;
+ printf "\n";
+ printf "%s\n\n" pod
+ ) args
+
+let list_operations () =
+ assert !baked;
+
+ (* For compatibility with old shell version, list just the operation
+ * names, sorted.
+ *)
+ List.iter (fun op -> print_endline op.name ) !ops
+
+module OperationSet = Set.Make (
+ struct
+ type t = operation
+ let compare a b = compare a.name b.name
+ end
+)
+type set = OperationSet.t
+
+let empty_set = OperationSet.empty
+
+let add_to_set name set =
+ assert !baked;
+
+ let op = List.find (fun { name = n } -> name = n) !ops in
+ OperationSet.add op set
+
+let perform_operations ?operations g root =
+ assert !baked;
+
+ let ops =
+ match operations with
+ | None -> !ops (* all operations *)
+ | Some opset -> (* just the operation names listed *)
+ OperationSet.elements opset in
+
+ let flags = List.map (fun op -> op.perform g root) ops in
+
+ List.flatten flags
diff --git a/sysprep/sysprep_operation.mli b/sysprep/sysprep_operation.mli
new file mode 100644
index 0000000..16d9285
--- /dev/null
+++ b/sysprep/sysprep_operation.mli
@@ -0,0 +1,96 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+(** Structure used to describe sysprep operations. *)
+
+type flag = [ `Created_files ]
+
+type operation = {
+ name : string;
+ (** Operation name, also used to enable the operation on the command
+ line. Must contain only alphanumeric and '-' (dash)
+ character. *)
+
+ pod_description : string;
+ (** POD-format description, used for the man page. *)
+
+ extra_args : ((Arg.key * Arg.spec * Arg.doc) * string) list;
+ (** Extra command-line arguments, if any. eg. The [hostname]
+ operation has an extra [--hostname] parameter.
+
+ Each element of the list is the argspec (see {!Arg.spec} etc.)
+ and the corresponding full POD documentation.
+
+ You can decide the types of the arguments, whether they are
+ mandatory etc. *)
+
+ perform : Guestfs.guestfs -> string -> flag list;
+ (** The function which is called to perform this operation, when
+ enabled.
+
+ The parameters are [g] (libguestfs handle) and [root] (the
+ operating system root filesystem). Inspection has been performed
+ already on this handle so if the operation depends on OS type,
+ call [g#inspect_get_type], [g#inspect_get_distro] etc. in order to
+ determine that. The guest operating system's disks have been
+ mounted up, and this function must not unmount them.
+
+ In the rare case of a multiboot operating system, it is possible
+ for this function to be called multiple times.
+
+ On success, the function can return a list of flags (or an
+ empty list). See {!flag}.
+
+ On error the function should raise an exception. The function
+ also needs to be careful to {i suppress} exceptions for things
+ which are not errors, eg. deleting non-existent files. *)
+}
+
+val register_operation : operation -> unit
+(** Register an operation. *)
+
+val bake : unit -> unit
+(** 'Bake' is called after all modules have been registered. We
+ finalize the list of operations, sort it, and run some checks. *)
+
+val dump_pod : unit -> unit
+(** Dump the perldoc (POD) for the manual page
+ (implements [--dump-pod]). *)
+
+val dump_pod_options : unit -> unit
+(** Dump the perldoc (POD) for the [extra_args]
+ (implements [--dump-pod-options]). *)
+
+val list_operations : unit -> unit
+(** List supported operations
+ (implements [--list-operations]). *)
+
+type set
+(** A (sub-)set of operations. *)
+
+val empty_set : set
+(** Empty set of operations. *)
+
+val add_to_set : string -> set -> set
+(** [add_to_set name set] adds the operation named [name] to [set].
+
+ Note that this will raise [Not_found] if [name] is not
+ a valid operation name. *)
+
+val perform_operations : ?operations:set -> Guestfs.guestfs -> string -> flag
list
+(** Perform all operations, or the subset listed in the [operations] set. *)
diff --git a/sysprep/sysprep_operation_hostname.ml
b/sysprep/sysprep_operation_hostname.ml
new file mode 100644
index 0000000..1472a1c
--- /dev/null
+++ b/sysprep/sysprep_operation_hostname.ml
@@ -0,0 +1,69 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open Utils
+open Sysprep_operation
+
+module G = Guestfs
+
+let hostname = ref "localhost.localdomain"
+
+let hostname_perform g root =
+ let typ = g#inspect_get_type root in
+ let distro = g#inspect_get_distro root in
+ match typ, distro with
+ | "linux", ("fedora"|"rhel") ->
+ (* Replace HOSTNAME=... entry. The code assumes it's a small,
+ * plain text file.
+ *)
+ let filename = "/etc/sysconfig/network" in
+ let lines = Array.to_list (g#read_lines filename) in
+ let lines = List.filter (
+ fun line -> not (string_prefix line "HOSTNAME=")
+ ) lines in
+ let file =
+ String.concat "\n" lines ^
+ sprintf "\nHOSTNAME=%s\n" !hostname in
+ g#write filename file;
+ [ `Created_files ]
+
+ | "linux", ("debian"|"ubuntu") ->
+ g#write "/etc/hostname" !hostname;
+ [ `Created_files ]
+
+ | _ -> []
+
+let hostname_op = {
+ name = "hostname";
+ pod_description = "\
+Changes the hostname of the guest to the value given in the I<--hostname>
+parameter.
+
+If the I<--hostname> parameter is not given, then the hostname is changed
+to C<localhost.localdomain>.";
+ extra_args = [
+ ("--hostname", Arg.Set_string hostname, "hostname New
hostname"),
+ "\
+Change the hostname. If not given, defaults to C<localhost.localdomain>."
+ ];
+ perform = hostname_perform;
+}
+
+let () = register_operation hostname_op
diff --git a/sysprep/sysprep_operation_utmp.ml b/sysprep/sysprep_operation_utmp.ml
new file mode 100644
index 0000000..69867e1
--- /dev/null
+++ b/sysprep/sysprep_operation_utmp.ml
@@ -0,0 +1,43 @@
+(* virt-sysprep
+ * Copyright (C) 2012 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Sysprep_operation
+
+module G = Guestfs
+
+let utmp_perform g root =
+ let typ = g#inspect_get_type root in
+ if typ <> "windows" then (
+ try g#rm "/var/run/utmp"
+ with G.Error _ -> ()
+ );
+ []
+
+let utmp_op = {
+ name = "utmp";
+ pod_description = "\
+Remove the utmp file.
+
+This file records who is currently logged in on a machine. In modern
+Linux distros it is stored in a ramdisk and hence not part of the
+virtual machine's disk, but it was stored on disk in older distros.";
+ extra_args = [];
+ perform = utmp_perform;
+}
+
+let () = register_operation utmp_op
diff --git a/sysprep/test-virt-sysprep.sh b/sysprep/test-virt-sysprep.sh
new file mode 100755
index 0000000..e0bae2d
--- /dev/null
+++ b/sysprep/test-virt-sysprep.sh
@@ -0,0 +1,26 @@
+#!/bin/bash -
+# libguestfs virt-sysprep test script
+# Copyright (C) 2011-2012 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+export LANG=C
+set -e
+
+if [ ! -w /dev/fuse ]; then
+ echo "SKIPPING virt-sysprep test, because there is no /dev/fuse."
+ exit 0
+fi
+
diff --git a/sysprep/utils.ml b/sysprep/utils.ml
new file mode 100644
index 0000000..7a4cbef
--- /dev/null
+++ b/sysprep/utils.ml
@@ -0,0 +1,69 @@
+(* virt-sysprep
+ * Copyright (C) 2010-2012 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+module G = Guestfs
+
+let (//) = Filename.concat
+
+let string_prefix str prefix =
+ let n = String.length prefix in
+ String.length str >= n && String.sub str 0 n = prefix
+
+let rec string_find s sub =
+ let len = String.length s in
+ let sublen = String.length sub in
+ let rec loop i =
+ if i <= len-sublen then (
+ let rec loop2 j =
+ if j < sublen then (
+ if s.[i+j] = sub.[j] then loop2 (j+1)
+ else -1
+ ) else
+ i (* found *)
+ in
+ let r = loop2 0 in
+ if r = -1 then loop (i+1) else r
+ ) else
+ -1 (* not found *)
+ in
+ loop 0
+
+let rec string_split sep str =
+ let len = String.length str in
+ let seplen = String.length sep in
+ let i = string_find str sep in
+ if i = -1 then [str]
+ else (
+ let s' = String.sub str 0 i in
+ let s'' = String.sub str (i+seplen) (len-i-seplen) in
+ s' :: string_split sep s''
+ )
+
+let string_random8 =
+ let chars = "abcdefghijklmnopqrstuvwxyz0123456789" in
+ fun () ->
+ String.concat "" (
+ List.map (
+ fun _ ->
+ let c =
Random.int 36 in
+ let c = chars.[c] in
+ String.make 1 c
+ ) [1;2;3;4;5;6;7;8]
+ )
diff --git a/sysprep/virt-sysprep.pod b/sysprep/virt-sysprep.pod
new file mode 100755
index 0000000..ca1b37b
--- /dev/null
+++ b/sysprep/virt-sysprep.pod
@@ -0,0 +1,419 @@
+=encoding utf8
+
+=head1 NAME
+
+virt-sysprep - Reset or unconfigure a virtual machine so clones can be made
+
+=head1 SYNOPSIS
+
+ virt-sysprep [--options] -d domname
+
+ virt-sysprep [--options] -a disk.img [-a disk.img ...]
+
+=head1 DESCRIPTION
+
+Virt-sysprep "resets" or "unconfigures" a virtual machine so that
+clones can be made from it. Steps in this process include removing
+SSH host keys, removing persistent network MAC configuration, and
+removing user accounts. Each step can be enabled or disabled as
+required.
+
+Virt-sysprep modifies the guest or disk image I<in place>. The guest
+must be shut down. If you want to preserve the existing contents of
+the guest, you I<must copy or clone the disk first>.
+See L</COPYING AND CLONING> below.
+
+You do I<not> need to run virt-sysprep as root. In fact we'd
+generally recommend that you don't. The time you might want to run it
+as root is when you need root in order to access the disk image, but
+even in this case it would be better to change the permissions on the
+disk image to be writable as the non-root user running virt-sysprep.
+
+"Sysprep" stands for "system preparation" tool. The name comes from
+the Microsoft program C<sysprep.exe> which is used to unconfigure
+Windows machines in preparation for cloning them. Having said that,
+virt-sysprep does I<not> currently work on Microsoft Windows guests.
+We plan to support Windows sysprepping in a future version, and we
+already have code to do it.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--help>
+
+Display brief help.
+
+=item B<-a> file
+
+=item B<--add> file
+
+Add I<file> which should be a disk image from a virtual machine.
+
+The format of the disk image is auto-detected. To override this and
+force a particular format use the I<--format> option.
+
+=item B<-c> URI
+
+=item B<--connect> URI
+
+If using libvirt, connect to the given I<URI>. If omitted, then we
+connect to the default libvirt hypervisor.
+
+If you specify guest block devices directly (I<-a>), then libvirt is
+not used at all.
+
+=item B<-d> guest
+
+=item B<--domain> guest
+
+Add all the disks from the named libvirt guest. Domain UUIDs can be
+used instead of names.
+
+=item B<-n>
+
+=item B<--dry-run>
+
+Perform a read-only "dry run" on the guest. This runs the sysprep
+operation, but throws away any changes to the disk at the end.
+
+=item B<--enable> operations
+
+Choose which sysprep operations to perform. Give a comma-separated
+list of operations, for example:
+
+ --enable ssh-hostkeys,udev-persistent-net
+
+would enable ONLY C<ssh-hostkeys> and C<udev-persistent-net> operations.
+
+If the I<--enable> option is not given, then we default to trying all
+possible sysprep operations. But some sysprep operations are skipped
+for some guest types.
+
+Use I<--list-operations> to list operations supported by a particular
+version of virt-sysprep.
+
+See L</OPERATIONS> below for a list and an explanation of each
+operation.
+
+=item B<--format> raw|qcow2|..
+
+=item B<--format> auto
+
+The default for the I<-a> option is to auto-detect the format of the
+disk image. Using this forces the disk format for I<-a> options which
+follow on the command line. Using I<--format auto> switches back to
+auto-detection for subsequent I<-a> options.
+
+For example:
+
+ virt-sysprep --format raw -a disk.img
+
+forces raw format (no auto-detection) for C<disk.img>.
+
+ virt-sysprep --format raw -a disk.img --format auto -a another.img
+
+forces raw format (no auto-detection) for C<disk.img> and reverts to
+auto-detection for C<another.img>.
+
+If you have untrusted raw-format guest disk images, you should use
+this option to specify the disk format. This avoids a possible
+security problem with malicious guests (CVE-2010-3851).
+
+=item B<--list-operations>
+
+List the operations supported by the virt-sysprep program.
+
+=item B<--selinux-relabel>
+
+=item B<--no-selinux-relabel>
+
+I<--selinux-relabel> forces SELinux relabelling next time the guest
+boots. I<--no-selinux-relabel> disables relabelling.
+
+The default is to try to detect if SELinux relabelling is required.
+See L</SELINUX RELABELLING> below for more details.
+
+=item B<-v>
+
+=item B<--verbose>
+
+Enable verbose messages for debugging.
+
+=item B<-V>
+
+=item B<--version>
+
+Display version number and exit.
+
+=item B<-x>
+
+Enable tracing of libguestfs API calls.
+
+@EXTRA_OPTIONS@
+
+=back
+
+=head1 OPERATIONS
+
+@OPERATIONS@
+
+=head1 COPYING AND CLONING
+
+Virt-sysprep can be used as part of a process of cloning guests, or to
+prepare a template from which guests can be cloned. There are many
+different ways to achieve this using the virt tools, and this section
+is just an introduction.
+
+A virtual machine (when switched off) consists of two parts:
+
+=over 4
+
+=item I<configuration>
+
+The configuration or description of the guest. eg. The libvirt
+XML (see C<virsh dumpxml>), the running configuration of the guest,
+or another external format like OVF.
+
+Some configuration items that might need to be changed:
+
+=over 4
+
+=item *
+
+name
+
+=item *
+
+UUID
+
+=item *
+
+path to block device(s)
+
+=item *
+
+network card MAC address
+
+=back
+
+=item I<block device(s)>
+
+One or more hard disk images, themselves containing files,
+directories, applications, kernels, configuration, etc.
+
+Some things inside the block devices that might need to be changed:
+
+=over 4
+
+=item *
+
+hostname and other net configuration
+
+=item *
+
+UUID
+
+=item *
+
+SSH host keys
+
+=item *
+
+Windows unique security ID (SID)
+
+=item *
+
+Puppet registration
+
+=back
+
+=back
+
+=head2 COPYING THE BLOCK DEVICE
+
+Starting with an original guest, you probably wish to copy the guest
+block device and its configuration to make a template. Then once you
+are happy with the template, you will want to make many clones from
+it.
+
+ virt-sysprep
+ |
+ v
+ original guest --------> template ---------->
+ \------> cloned
+ \-----> guests
+ \---->
+
+You can, of course, just copy the block device on the host using
+L<cp(1)> or L<dd(1)>.
+
+ dd dd
+ original guest --------> template ---------->
+ \------> cloned
+ \-----> guests
+ \---->
+
+There are some smarter (and faster) ways too:
+
+=over 4
+
+=item *
+
+ snapshot
+ template ---------->
+ \------> cloned
+ \-----> guests
+ \---->
+
+Use the block device as a backing file and create a snapshot on top
+for each guest. The advantage is that you don't need to copy the
+block device (very fast) and only changes are stored (less storage
+required).
+
+Note that writing to the backing file once you have created guests on
+top of it is not possible: you will corrupt the guests.
+
+Tools that can do this include:
+L<qemu-img(1)> (with the I<create -f qcow2 -o backing_file> option),
+L<lvcreate(8)> (I<--snapshot> option). Some filesystems (such as
+btrfs) and most Network Attached Storage devices can also create cheap
+snapshots from files or LUNs.
+
+=item *
+
+Get your NAS to snapshot and/or duplicate the LUN.
+
+=item *
+
+Prepare your template using L<virt-sparsify(1)>. See below.
+
+=back
+
+=head2 VIRT-CLONE
+
+A separate tool, L<virt-clone(1)>, can be used to duplicate the block
+device and/or modify the external libvirt configuration of a guest.
+It will reset the name, UUID and MAC address of the guest in the
+libvirt XML.
+
+L<virt-clone(1)> does not use libguestfs and cannot look inside the
+disk image. This was the original motivation to write virt-sysprep.
+
+=head2 SPARSIFY
+
+ virt-sparsify
+ original guest --------> template
+
+L<virt-sparsify(1)> can be used to make the cloning template smaller,
+making it easier to compress and/or faster to copy.
+
+Notice that since virt-sparsify also copies the image, you can use it
+to make the initial copy (instead of C<dd>).
+
+=head2 RESIZE
+
+ virt-resize
+ template ---------->
+ \------> cloned
+ \-----> guests
+ \---->
+
+If you want to give people cloned guests, but let them pick the size
+of the guest themselves (eg. depending on how much they are prepared
+to pay for disk space), then instead of copying the template, you can
+run L<virt-resize(1)>. Virt-resize performs a copy and resize, and
+thus is ideal for cloning guests from a template.
+
+=head1 SECURITY
+
+Although virt-sysprep removes some sensitive information from the
+guest, it does not pretend to remove all of it. You should examine
+the L</OPERATIONS> above and the guest afterwards.
+
+Sensitive files are simply removed. The data they contained may still
+exist on the disk, easily recovered with a hex editor or undelete
+tool. Use L<virt-sparsify(1)> as one way to remove this content. See
+also the L<scrub(1)> command to get rid of deleted content in
+directory entries and inodes.
+
+=head2 RANDOM SEED
+
+I<(This section applies to Linux guests only)>
+
+The virt-sysprep C<random-seed> operation writes a few bytes of
+randomness from the host into the guest's random seed file.
+
+If this is just done once and the guest is cloned from the same
+template, then each guest will start with the same entropy, and things
+like SSH host keys and TCP sequence numbers may be predictable.
+
+Therefore you should arrange to add more randomness I<after> cloning
+from a template too, which can be done by just enabling the
+C<random-seed> operation:
+
+ cp template.img newguest.img
+ virt-sysprep --enable random-seed -a newguest.img
+
+=head2 SELINUX RELABELLING
+
+I<(This section applies to Linux guests using SELinux only)>
+
+If any new files are created by virt-sysprep, then virt-sysprep
+touches C</.autorelabel> so that these will be correctly labelled by
+SELinux the next time the guest is booted. This process interrupts
+boot and can take some time.
+
+You can force relabelling for all guests by supplying the
+I<--selinux-relabel> option.
+
+You can disable relabelling entirely by supplying the
+I<--no-selinux-relabel> option.
+
+=head1 SHELL QUOTING
+
+Libvirt guest names can contain arbitrary characters, some of which
+have meaning to the shell such as C<#> and space. You may need to
+quote or escape these characters on the command line. See the shell
+manual page L<sh(1)> for details.
+
+=head1 EXIT STATUS
+
+This program returns 0 on success, or 1 if there was an error.
+
+=head1 SEE ALSO
+
+L<guestfs(3)>,
+L<guestfish(1)>,
+L<virt-clone(1)>,
+L<virt-rescue(1)>,
+L<virt-resize(1)>,
+L<virt-sparsify(1)>,
+L<virsh(1)>,
+L<lvcreate(8)>,
+L<qemu-img(1)>,
+L<scrub(1)>,
+L<http://libguestfs.org/>,
+L<http://libvirt.org/>.
+
+=head1 AUTHOR
+
+Richard W.M. Jones
L<http://people.redhat.com/~rjones/>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2011-2012 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
diff --git a/tests/extra/Makefile.am b/tests/extra/Makefile.am
index b807048..baba4a3 100644
--- a/tests/extra/Makefile.am
+++ b/tests/extra/Makefile.am
@@ -28,10 +28,8 @@
#
# XXX Not tested:
#
-# ../clone/virt-sysprep
-# - hard to test because it's a shell script
-#
# ../edit/virt-edit
+# ../sysprep/virt-sysprep
#
# Perl bindings
# ../edit/virt-edit -e
--
1.7.9.3