Add virt-customize standalone tool.

This includes some simple tests and a manual page.
This commit is contained in:
Richard W.M. Jones
2014-03-25 10:46:08 +00:00
parent aa3bc8b65d
commit 2b208d84db
13 changed files with 474 additions and 8 deletions

3
.gitignore vendored
View File

@@ -95,7 +95,9 @@ Makefile.in
/customize/customize_cmdline.mli
/customize/customize-options.pod
/customize/customize-synopsis.pod
/customize/stamp-virt-customize.pod
/customize/virt-customize
/customize/virt-customize.1
/daemon/actions.h
/daemon/errnostring.c
/daemon/errnostring-gperf.c
@@ -236,6 +238,7 @@ Makefile.in
/html/virt-cat.1.html
/html/virt-copy-in.1.html
/html/virt-copy-out.1.html
/html/virt-customize.1.html
/html/virt-df.1.html
/html/virt-diff.1.html
/html/virt-edit.1.html

View File

@@ -239,6 +239,7 @@ HTMLFILES = \
html/virt-cat.1.html \
html/virt-copy-in.1.html \
html/virt-copy-out.1.html \
html/virt-customize.1.html \
html/virt-df.1.html \
html/virt-diff.1.html \
html/virt-edit.1.html \

View File

@@ -1590,6 +1590,7 @@ L<guestfs(3)>,
L<guestfish(1)>,
L<guestmount(1)>,
L<virt-copy-out(1)>,
L<virt-customize(1)>,
L<virt-install(1)>,
L<virt-rescue(1)>,
L<virt-resize(1)>,

View File

@@ -18,9 +18,14 @@
include $(top_srcdir)/subdir-rules.mk
EXTRA_DIST = \
$(SOURCES)
$(SOURCES) \
test-virt-customize.sh \
virt-customize.pod
CLEANFILES = *~ *.cmi *.cmo *.cmx *.cmxa *.o
CLEANFILES = \
*~ *.cmi *.cmo *.cmx *.cmxa *.o \
stamp-virt-customize.pod \
virt-customize virt-customize.1
generator_built = \
customize_cmdline.mli \
@@ -41,6 +46,7 @@ SOURCES = \
firstboot.mli \
hostname.ml \
hostname.mli \
main.ml \
password.ml \
password.mli \
perl_edit.ml \
@@ -55,8 +61,12 @@ SOURCES = \
if HAVE_OCAML
deps = \
$(top_builddir)/fish/guestfish-uri.o \
$(top_builddir)/mllib/common_gettext.cmx \
$(top_builddir)/mllib/common_utils.cmx \
$(top_builddir)/mllib/config.cmx \
$(top_builddir)/mllib/uri-c.o \
$(top_builddir)/mllib/uRI.cmx \
crypt-c.o
if HAVE_OCAMLOPT
@@ -76,7 +86,8 @@ ocaml_modules = \
random_seed \
timezone \
customize_cmdline \
customize_run
customize_run \
main
if HAVE_OCAMLOPT
OBJECTS += $(patsubst %,%.cmx,$(ocaml_modules))
@@ -84,9 +95,7 @@ else
OBJECTS += $(patsubst %,%.cmo,$(ocaml_modules))
endif
# XXX virt-customize isn't a complete tool yet, so currently this is
# just a dummy target binary.
noinst_SCRIPTS = virt-customize
bin_SCRIPTS = virt-customize
# -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
@@ -144,11 +153,29 @@ DEFAULT_INCLUDES = \
.c.o:
$(CC) $(CFLAGS) $(PROF_CFLAGS) $(DEFAULT_INCLUDES) -c $< -o $@
# Manual pages and HTML files for the website.
man_MANS = virt-customize.1
noinst_DATA = $(top_builddir)/html/virt-customize.1.html
virt-customize.1 $(top_builddir)/html/virt-customize.1.html: stamp-virt-customize.pod
stamp-virt-customize.pod: virt-customize.pod $(top_srcdir)/customize/customize-synopsis.pod $(top_srcdir)/customize/customize-options.pod
$(PODWRAPPER) \
--man virt-customize.1 \
--html $(top_builddir)/html/virt-customize.1.html \
--insert $(top_srcdir)/customize/customize-synopsis.pod:__CUSTOMIZE_SYNOPSIS__ \
--insert $(top_srcdir)/customize/customize-options.pod:__CUSTOMIZE_OPTIONS__ \
--license GPLv2+ \
$<
touch $@
# Tests.
TESTS_ENVIRONMENT = $(top_builddir)/run --test
TESTS =
if ENABLE_APPLIANCE
TESTS = test-virt-customize.sh
endif
check-valgrind:
$(MAKE) VG="$(top_builddir)/run @VG@" check

222
customize/main.ml Normal file
View File

@@ -0,0 +1,222 @@
(* virt-customize
* Copyright (C) 2014 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 Common_gettext.Gettext
open Common_utils
open Printf
module G = Guestfs
let () = Random.self_init ()
let prog = Filename.basename Sys.executable_name
let main () =
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 quiet = ref false in
let libvirturi = ref "" in
let trace = ref false in
let verbose = ref false in
let display_version () =
printf "virt-customize %s\n" Config.package_version;
exit 0
and add_file arg =
let uri =
try URI.parse_uri arg
with Invalid_argument "URI.parse_uri" ->
eprintf "Error parsing URI '%s'. Look for error messages printed above.\n" arg;
exit 1 in
let format = match !format with "auto" -> None | fmt -> Some fmt in
files := (uri, format) :: !files
and set_domain dom =
if !domain <> None then (
eprintf (f_"%s: --domain option can only be given once\n") prog;
exit 1
);
domain := Some dom
in
let argspec = [
"-a", Arg.String add_file, s_"file" ^ " " ^ s_"Add disk image file";
"--add", Arg.String add_file, s_"file" ^ " " ^ s_"Add disk image file";
"-c", Arg.Set_string libvirturi, s_"uri" ^ " " ^ s_"Set libvirt URI";
"--connect", Arg.Set_string libvirturi, s_"uri" ^ " " ^ s_"Set libvirt URI";
"--debug-gc", Arg.Set debug_gc, " " ^ s_"Debug GC and memory allocations (internal)";
"-d", Arg.String set_domain, s_"domain" ^ " " ^ s_"Set libvirt guest name";
"--domain", Arg.String set_domain, s_"domain" ^ " " ^ s_"Set libvirt guest name";
"-n", Arg.Set dryrun, " " ^ s_"Perform a dry run";
"--dryrun", Arg.Set dryrun, " " ^ s_"Perform a dry run";
"--dry-run", Arg.Set dryrun, " " ^ s_"Perform a dry run";
"--format", Arg.Set_string format, s_"format" ^ " " ^ s_"Set format (default: auto)";
"--long-options", Arg.Unit display_long_options, " " ^ s_"List long options";
"-q", Arg.Set quiet, " " ^ s_"Don't print log messages";
"--quiet", Arg.Set quiet, " " ^ s_"Don't print log messages";
"-v", Arg.Set verbose, " " ^ s_"Enable debugging messages";
"--verbose", Arg.Set verbose, " " ^ s_"Enable debugging messages";
"-V", Arg.Unit display_version, " " ^ s_"Display version and exit";
"--version", Arg.Unit display_version, " " ^ s_"Display version and exit";
"-x", Arg.Set trace, " " ^ s_"Enable tracing of libguestfs calls";
] in
let customize_argspec, get_customize_ops =
Customize_cmdline.argspec ~prog () in
let customize_argspec =
List.map (fun (spec, _, _) -> spec) customize_argspec in
let argspec = argspec @ customize_argspec in
let argspec =
let cmp (arg1, _, _) (arg2, _, _) =
let arg1 = skip_dashes arg1 and arg2 = skip_dashes arg2 in
compare (String.lowercase arg1) (String.lowercase arg2)
in
List.sort cmp argspec in
let argspec = Arg.align argspec in
long_options := argspec;
let anon_fun _ = raise (Arg.Bad (s_"extra parameter on the command line")) in
let usage_msg =
sprintf (f_"\
%s: customize a virtual machine
virt-customize [--options] -d domname
virt-customize [--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-customize(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 (f_"%s: you must give either -a or -d options\n") prog;
eprintf (f_"Read virt-customize(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
let discard = if readonly then None else Some "besteffort" in
ignore (g#add_domain
~readonly ?discard
?libvirturi ~allowuuid ~readonlydisk
dom)
| _, Some _ ->
eprintf (f_"%s: you cannot give -a and -d options together\n") prog;
eprintf (f_"Read virt-customize(1) man page for further information.\n");
exit 1
| files, None ->
fun g readonly ->
List.iter (
fun (uri, format) ->
let { URI.path = path; protocol = protocol;
server = server; username = username } = uri in
let discard = if readonly then None else Some "besteffort" in
g#add_drive
~readonly ?discard
?format ~protocol ?server ?username
path
) files
in
(* Dereference the rest of the args. *)
let debug_gc = !debug_gc in
let dryrun = !dryrun in
let quiet = !quiet in
let trace = !trace in
let verbose = !verbose in
let ops = get_customize_ops () in
let msg fs = make_message_function ~quiet fs in
msg (f_"Examining the guest ...");
(* 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 ();
(* Inspection. *)
(match Array.to_list (g#inspect_os ()) with
| [] ->
eprintf (f_"%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 (f_"%s (ignored)\n") msg
) mps;
(* Do the customization. *)
Customize_run.run ~prog ~debug:verbose ~quiet g root ops;
g#umount_all ();
) roots;
);
g#shutdown ();
g#close ();
if debug_gc then
Gc.compact ()
(* Finished. *)
let () =
(try main ()
with
| Failure msg -> (* from failwith/failwithf *)
eprintf (f_"%s: %s\n") prog msg;
exit 1
| Invalid_argument msg -> (* probably should never happen *)
eprintf (f_"%s: internal error: invalid argument: %s\n") prog msg;
exit 1
| Assert_failure (file, line, char) -> (* should never happen *)
eprintf (f_"%s: internal error: assertion failed at %s, line %d, char %d\n")
prog file line char;
exit 1
| Not_found -> (* should never happen *)
eprintf (f_"%s: internal error: Not_found exception was thrown\n") prog;
exit 1
| exn ->
eprintf (f_"%s: exception: %s\n") prog (Printexc.to_string exn);
exit 1
);
exit 0

View File

@@ -0,0 +1,32 @@
#!/bin/bash -
# libguestfs virt-customize test script
# Copyright (C) 2014 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
# virt-customize with the -n option doesn't modify the guest. It ought
# to be able to customize any of our Linux-like test guests.
for f in ../tests/guests/{debian,fedora,ubuntu}.img; do
# Ignore zero-sized windows.img if ntfs-3g is not installed.
if [ -s "$f" ]; then
$VG ./virt-customize -n -a $f \
--write /etc/motd:HELLO \
--delete /etc/motd
fi
done

View File

@@ -0,0 +1,156 @@
=head1 NAME
virt-customize - Customize a virtual machine
=head1 SYNOPSIS
virt-customize [--options] -d domname
__CUSTOMIZE_SYNOPSIS__
virt-customize [--options] -a disk.img [-a disk.img ...]
__CUSTOMIZE_SYNOPSIS__
=head1 DESCRIPTION
Virt-customize can customize a virtual machine (disk image) by
installing packages, editing configuration files, and so on.
Virt-customize 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, I<you must snapshot, copy or clone the disk first>.
You do I<not> need to run virt-customize as root. In fact we'd
generally recommend that you don't.
Related tools include: L<virt-sysprep(1)> and L<virt-builder(1)>.
=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<-a> URI
=item B<--add> URI
Add a remote disk. The URI format is compatible with guestfish.
See L<guestfish(1)/ADDING REMOTE STORAGE>.
=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<--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-customize --format raw -a disk.img
forces raw format (no auto-detection) for C<disk.img>.
virt-customize --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<-q>
=item B<--quiet>
Don't print log messages.
To enable detailed logging of individual file operations, use I<-x>.
=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
=head2 Customization options
__CUSTOMIZE_OPTIONS__
=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-builder(1)>,
L<virt-clone(1)>,
L<virt-rescue(1)>,
L<virt-resize(1)>,
L<virt-sparsify(1)>,
L<virt-sysprep(1)>,
L<virsh(1)>,
L<lvcreate(8)>,
L<qemu-img(1)>,
L<scrub(1)>,
L<http://libguestfs.org/>,
L<http://libvirt.org/>.
=head1 AUTHORS
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
=head1 COPYRIGHT
Copyright (C) 2011-2014 Red Hat Inc.

View File

@@ -1607,6 +1607,7 @@ L<virt-builder(1)>,
L<virt-cat(1)>,
L<virt-copy-in(1)>,
L<virt-copy-out(1)>,
L<virt-customize(1)>,
L<virt-df(1)>,
L<virt-diff(1)>,
L<virt-edit(1)>,

View File

@@ -116,6 +116,15 @@ virt-builder.1: virt-builder.pod customize-synopsis.pod customize-options.pod
--insert $(srcdir)/customize-options.pod:__CUSTOMIZE_OPTIONS__ \
$<
virt-customize.1: virt-customize.pod customize-synopsis.pod customize-options.pod
$(PODWRAPPER) \
--no-strict-checks \
--man $@ \
--license GPLv2+ \
--insert $(srcdir)/customize-synopsis.pod:__CUSTOMIZE_SYNOPSIS__ \
--insert $(srcdir)/customize-options.pod:__CUSTOMIZE_OPTIONS__ \
$<
virt-sysprep.1: virt-sysprep.pod sysprep-extra-options.pod sysprep-operations.pod
$(PODWRAPPER) \
--no-strict-checks \

View File

@@ -5,6 +5,9 @@
../cat/virt-cat.pod
../cat/virt-filesystems.pod
../cat/virt-ls.pod
../customize/customize-options.pod
../customize/customize-synopsis.pod
../customize/virt-customize.pod
../daemon/guestfsd.pod
../df/virt-df.pod
../diff/virt-diff.pod

View File

@@ -116,6 +116,15 @@ virt-builder.1: virt-builder.pod customize-synopsis.pod customize-options.pod
--insert $(srcdir)/customize-options.pod:__CUSTOMIZE_OPTIONS__ \
$<
virt-customize.1: virt-customize.pod customize-synopsis.pod customize-options.pod
$(PODWRAPPER) \
--no-strict-checks \
--man $@ \
--license GPLv2+ \
--insert $(srcdir)/customize-synopsis.pod:__CUSTOMIZE_SYNOPSIS__ \
--insert $(srcdir)/customize-options.pod:__CUSTOMIZE_OPTIONS__ \
$<
virt-sysprep.1: virt-sysprep.pod sysprep-extra-options.pod sysprep-operations.pod
$(PODWRAPPER) \
--no-strict-checks \

View File

@@ -4274,7 +4274,7 @@ Outside contributions, experimental parts.
=item C<customize>
virt-customize mini-library.
L<virt-customize(1)> command and documentation.
=item C<daemon>
@@ -4735,6 +4735,7 @@ L<virt-builder(1)>,
L<virt-cat(1)>,
L<virt-copy-in(1)>,
L<virt-copy-out(1)>,
L<virt-customize(1)>,
L<virt-df(1)>,
L<virt-diff(1)>,
L<virt-edit(1)>,

View File

@@ -528,6 +528,7 @@ L<guestfs(3)>,
L<guestfish(1)>,
L<virt-builder(1)>,
L<virt-clone(1)>,
L<virt-customize(1)>,
L<virt-rescue(1)>,
L<virt-resize(1)>,
L<virt-sparsify(1)>,