mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
New tool: virt-builder: For quickly building virtual machine images.
On baremetal you can build and customize a new guest in under 2
minutes. For example:
$ virt-builder fedora-19 \
--root-password password:test \
--install minicom \
--firstboot-command 'yum -y update' \
--firstboot-command 'useradd -m -p "" rjones ; chage -d 0 rjones'
[ 0.0] Downloading: file:///home/rjones/d/libguestfs/builder/website/fedora-19.xz
[ 1.0] Uncompressing: file:///home/rjones/d/libguestfs/builder/website/fedora-19.xz
[ 24.0] Running virt-resize to expand the disk to 4.2G
[ 77.0] Opening the new disk
[ 81.0] Installing packages: minicom
[ 94.0] Installing firstboot command: [001] yum -y update
[ 94.0] Installing firstboot command: [002] useradd -m -p "" rjones ; chage -d 0 rjones
[ 94.0] Finishing off
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -46,6 +46,7 @@ Makefile.in
|
|||||||
/appliance/stamp-supermin
|
/appliance/stamp-supermin
|
||||||
/appliance/supermin.d
|
/appliance/supermin.d
|
||||||
/autom4te.cache
|
/autom4te.cache
|
||||||
|
/bash/virt-builder
|
||||||
/bash/virt-cat
|
/bash/virt-cat
|
||||||
/bash/virt-df
|
/bash/virt-df
|
||||||
/bash/virt-edit
|
/bash/virt-edit
|
||||||
@@ -56,6 +57,10 @@ Makefile.in
|
|||||||
/bash/virt-sysprep
|
/bash/virt-sysprep
|
||||||
/bash/virt-sparsify
|
/bash/virt-sparsify
|
||||||
/build-aux
|
/build-aux
|
||||||
|
/builder/.depend
|
||||||
|
/builder/stamp-virt-builder.pod
|
||||||
|
/builder/virt-builder
|
||||||
|
/builder/virt-builder.1
|
||||||
/cat/stamp-virt-*.pod
|
/cat/stamp-virt-*.pod
|
||||||
/cat/virt-cat
|
/cat/virt-cat
|
||||||
/cat/virt-cat.1
|
/cat/virt-cat.1
|
||||||
@@ -206,6 +211,7 @@ Makefile.in
|
|||||||
/html/libguestfs-make-fixed-appliance.1.html
|
/html/libguestfs-make-fixed-appliance.1.html
|
||||||
/html/libguestfs-test-tool.1.html
|
/html/libguestfs-test-tool.1.html
|
||||||
/html/virt-alignment-scan.1.html
|
/html/virt-alignment-scan.1.html
|
||||||
|
/html/virt-builder.1.html
|
||||||
/html/virt-cat.1.html
|
/html/virt-cat.1.html
|
||||||
/html/virt-copy-in.1.html
|
/html/virt-copy-in.1.html
|
||||||
/html/virt-copy-out.1.html
|
/html/virt-copy-out.1.html
|
||||||
@@ -264,6 +270,7 @@ Makefile.in
|
|||||||
/mllib/common_gettext.ml
|
/mllib/common_gettext.ml
|
||||||
/mllib/common_utils_tests
|
/mllib/common_utils_tests
|
||||||
/mllib/dummy
|
/mllib/dummy
|
||||||
|
/mllib/libdir.ml
|
||||||
/ocaml/bindtests.bc
|
/ocaml/bindtests.bc
|
||||||
/ocaml/bindtests.opt
|
/ocaml/bindtests.opt
|
||||||
/ocaml/bindtests.ml
|
/ocaml/bindtests.ml
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ SUBDIRS += csharp
|
|||||||
# OCaml tools. Note 'mllib' contains random shared code used by
|
# OCaml tools. Note 'mllib' contains random shared code used by
|
||||||
# all of the OCaml tools.
|
# all of the OCaml tools.
|
||||||
if HAVE_OCAML
|
if HAVE_OCAML
|
||||||
SUBDIRS += mllib resize sparsify sysprep
|
SUBDIRS += mllib builder resize sparsify sysprep
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Perl tools.
|
# Perl tools.
|
||||||
@@ -206,6 +206,7 @@ HTMLFILES = \
|
|||||||
html/libguestfs-make-fixed-appliance.1.html \
|
html/libguestfs-make-fixed-appliance.1.html \
|
||||||
html/libguestfs-test-tool.1.html \
|
html/libguestfs-test-tool.1.html \
|
||||||
html/virt-alignment-scan.1.html \
|
html/virt-alignment-scan.1.html \
|
||||||
|
html/virt-builder.1.html \
|
||||||
html/virt-cat.1.html \
|
html/virt-cat.1.html \
|
||||||
html/virt-copy-in.1.html \
|
html/virt-copy-in.1.html \
|
||||||
html/virt-copy-out.1.html \
|
html/virt-copy-out.1.html \
|
||||||
@@ -275,7 +276,7 @@ all-local:
|
|||||||
grep -v -E '/((guestfs|rc)_protocol\.c)$$' | \
|
grep -v -E '/((guestfs|rc)_protocol\.c)$$' | \
|
||||||
LC_ALL=C sort > po/POTFILES
|
LC_ALL=C sort > po/POTFILES
|
||||||
cd $(srcdir); \
|
cd $(srcdir); \
|
||||||
find mllib resize sparsify sysprep -name '*.ml' | \
|
find builder mllib resize sparsify sysprep -name '*.ml' | \
|
||||||
LC_ALL=C sort > po/POTFILES-ml
|
LC_ALL=C sort > po/POTFILES-ml
|
||||||
|
|
||||||
# Manual pages in top level directory.
|
# Manual pages in top level directory.
|
||||||
|
|||||||
10
README
10
README
@@ -162,6 +162,16 @@ The full requirements are described below.
|
|||||||
+--------------+-------------+---+-----------------------------------------+
|
+--------------+-------------+---+-----------------------------------------+
|
||||||
| uml_mkcow | | O | For the UML backend. |
|
| uml_mkcow | | O | For the UML backend. |
|
||||||
+--------------+-------------+---+-----------------------------------------+
|
+--------------+-------------+---+-----------------------------------------+
|
||||||
|
| curl | | O | Used by virt-builder for downloads |
|
||||||
|
+--------------+-------------+---+-----------------------------------------+
|
||||||
|
| gpg | | O | Used by virt-builder for digital |
|
||||||
|
| | | | signatures |
|
||||||
|
+--------------+-------------+---+-----------------------------------------+
|
||||||
|
| xz | | O | Used by virt-builder for compression |
|
||||||
|
+--------------+-------------+---+-----------------------------------------+
|
||||||
|
| nbdkit | | O | Used by virt-builder to speed up |
|
||||||
|
| | | | template xz-decompression |
|
||||||
|
+--------------+-------------+---+-----------------------------------------+
|
||||||
| findlib | | O | For the OCaml bindings. |
|
| findlib | | O | For the OCaml bindings. |
|
||||||
+--------------+-------------+---+-----------------------------------------+
|
+--------------+-------------+---+-----------------------------------------+
|
||||||
| ocaml-gettext| | O | For localizing OCaml virt-* tools. |
|
| ocaml-gettext| | O | For localizing OCaml virt-* tools. |
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ scripts = \
|
|||||||
guestfish \
|
guestfish \
|
||||||
guestmount \
|
guestmount \
|
||||||
virt-alignment-scan \
|
virt-alignment-scan \
|
||||||
|
virt-builder \
|
||||||
virt-cat \
|
virt-cat \
|
||||||
virt-df \
|
virt-df \
|
||||||
virt-edit \
|
virt-edit \
|
||||||
@@ -55,6 +56,8 @@ virt-ls:
|
|||||||
virt-sysprep:
|
virt-sysprep:
|
||||||
ln -sf virt-alignment-scan $@
|
ln -sf virt-alignment-scan $@
|
||||||
|
|
||||||
|
virt-builder:
|
||||||
|
ln -sf virt-resize $@
|
||||||
virt-sparsify:
|
virt-sparsify:
|
||||||
ln -sf virt-resize $@
|
ln -sf virt-resize $@
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# virt-resize, virt-sparsify bash completion script -*- shell-script -*-
|
# virt-resize, virt-builder, virt-sparsify bash completion script
|
||||||
|
# -*- shell-script -*-
|
||||||
# Copyright (C) 2010-2013 Red Hat Inc.
|
# Copyright (C) 2010-2013 Red Hat Inc.
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
@@ -33,6 +34,12 @@ _guestfs_options_only ()
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_virt_builder ()
|
||||||
|
{
|
||||||
|
_guestfs_options_only "$(virt-builder --long-options)"
|
||||||
|
} &&
|
||||||
|
complete -o default -F _virt_builder virt-builder
|
||||||
|
|
||||||
_virt_resize ()
|
_virt_resize ()
|
||||||
{
|
{
|
||||||
_guestfs_options_only "$(virt-resize --long-options)"
|
_guestfs_options_only "$(virt-resize --long-options)"
|
||||||
|
|||||||
161
builder/Makefile.am
Normal file
161
builder/Makefile.am
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
# libguestfs virt-builder tool
|
||||||
|
# Copyright (C) 2013 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 = \
|
||||||
|
$(SOURCES) \
|
||||||
|
virt-builder.pod \
|
||||||
|
test-virt-builder.sh \
|
||||||
|
website/.gitignore \
|
||||||
|
website/README \
|
||||||
|
website/index \
|
||||||
|
website/index.asc \
|
||||||
|
website/fedora-18.ks \
|
||||||
|
website/fedora-18.sh \
|
||||||
|
website/fedora-18.xz.sig \
|
||||||
|
website/fedora-19.ks \
|
||||||
|
website/fedora-19.sh \
|
||||||
|
website/fedora-19.xz.sig
|
||||||
|
|
||||||
|
CLEANFILES = *~ *.cmi *.cmo *.cmx *.cmxa *.o virt-builder
|
||||||
|
|
||||||
|
# Alphabetical order.
|
||||||
|
SOURCES = \
|
||||||
|
builder.ml \
|
||||||
|
downloader.mli \
|
||||||
|
downloader.ml \
|
||||||
|
get_kernel.mli \
|
||||||
|
get_kernel.ml \
|
||||||
|
index_parser.mli \
|
||||||
|
index_parser.ml \
|
||||||
|
list_entries.mli \
|
||||||
|
list_entries.ml \
|
||||||
|
sigchecker.mli \
|
||||||
|
sigchecker.ml
|
||||||
|
|
||||||
|
if HAVE_OCAML
|
||||||
|
|
||||||
|
# Note this list must be in dependency order.
|
||||||
|
OBJECTS = \
|
||||||
|
$(top_builddir)/mllib/libdir.cmx \
|
||||||
|
$(top_builddir)/mllib/common_gettext.cmx \
|
||||||
|
$(top_builddir)/mllib/common_utils.cmx \
|
||||||
|
$(top_builddir)/mllib/random_seed.cmx \
|
||||||
|
$(top_builddir)/mllib/firstboot.cmx \
|
||||||
|
$(top_builddir)/mllib/crypt-c.o \
|
||||||
|
$(top_builddir)/mllib/crypt.cmx \
|
||||||
|
$(top_builddir)/mllib/password.cmx \
|
||||||
|
get_kernel.cmx \
|
||||||
|
downloader.cmx \
|
||||||
|
sigchecker.cmx \
|
||||||
|
index_parser.cmx \
|
||||||
|
list_entries.cmx \
|
||||||
|
builder.cmx
|
||||||
|
|
||||||
|
bin_SCRIPTS = virt-builder
|
||||||
|
|
||||||
|
# -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 str,unix \
|
||||||
|
-I $(top_builddir)/src/.libs \
|
||||||
|
-I $(top_builddir)/ocaml \
|
||||||
|
-I $(top_builddir)/mllib
|
||||||
|
if HAVE_OCAML_PKG_GETTEXT
|
||||||
|
OCAMLPACKAGES += -package gettext-stub
|
||||||
|
endif
|
||||||
|
|
||||||
|
OCAMLCFLAGS = -g -warn-error CDEFLMPSUVYZX $(OCAMLPACKAGES)
|
||||||
|
OCAMLOPTFLAGS = $(OCAMLCFLAGS)
|
||||||
|
|
||||||
|
virt-builder: $(OBJECTS)
|
||||||
|
$(OCAMLFIND) ocamlopt $(OCAMLOPTFLAGS) \
|
||||||
|
mlguestfs.cmxa -linkpkg $^ \
|
||||||
|
-cclib '-lncurses -lcrypt' \
|
||||||
|
$(OCAML_GCOV_LDFLAGS) \
|
||||||
|
-o $@
|
||||||
|
|
||||||
|
.mli.cmi:
|
||||||
|
$(OCAMLFIND) ocamlc $(OCAMLCFLAGS) -c $< -o $@
|
||||||
|
.ml.cmo:
|
||||||
|
$(OCAMLFIND) ocamlc $(OCAMLCFLAGS) -c $< -o $@
|
||||||
|
.ml.cmx:
|
||||||
|
$(OCAMLFIND) ocamlopt $(OCAMLOPTFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
# automake will decide we don't need C support in this file. Really
|
||||||
|
# we do, so we have to provide it ourselves.
|
||||||
|
|
||||||
|
DEFAULT_INCLUDES = \
|
||||||
|
-I. \
|
||||||
|
-I$(top_builddir) \
|
||||||
|
-I$(shell $(OCAMLC) -where) \
|
||||||
|
-I$(top_srcdir)/src \
|
||||||
|
-I$(top_srcdir)/fish
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
$(CC) $(CFLAGS) $(PROF_CFLAGS) $(DEFAULT_INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
|
# Manual pages and HTML files for the website.
|
||||||
|
|
||||||
|
man_MANS = virt-builder.1
|
||||||
|
|
||||||
|
noinst_DATA = $(top_builddir)/html/virt-builder.1.html
|
||||||
|
|
||||||
|
virt-builder.1 $(top_builddir)/html/virt-builder.1.html: stamp-virt-builder.pod
|
||||||
|
|
||||||
|
stamp-virt-builder.pod: virt-builder.pod
|
||||||
|
$(PODWRAPPER) \
|
||||||
|
--man virt-builder.1 \
|
||||||
|
--html $(top_builddir)/html/virt-builder.1.html \
|
||||||
|
--license GPLv2+ \
|
||||||
|
$<
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
CLEANFILES += stamp-virt-builder.pod
|
||||||
|
|
||||||
|
# Tests.
|
||||||
|
|
||||||
|
TESTS_ENVIRONMENT = $(top_builddir)/run --test
|
||||||
|
|
||||||
|
if ENABLE_APPLIANCE
|
||||||
|
TESTS = test-virt-builder.sh
|
||||||
|
endif ENABLE_APPLIANCE
|
||||||
|
|
||||||
|
check-valgrind:
|
||||||
|
$(MAKE) VG="$(top_builddir)/run @VG@" check
|
||||||
|
|
||||||
|
# Dependencies.
|
||||||
|
depend: .depend
|
||||||
|
|
||||||
|
.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml)
|
||||||
|
rm -f $@ $@-t
|
||||||
|
$(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(top_srcdir)/mllib $^ | \
|
||||||
|
$(SED) 's/ *$$//' | \
|
||||||
|
$(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \
|
||||||
|
$(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \
|
||||||
|
sort > $@-t
|
||||||
|
mv $@-t $@
|
||||||
|
|
||||||
|
-include .depend
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
DISTCLEANFILES = .depend
|
||||||
|
|
||||||
|
.PHONY: depend docs
|
||||||
721
builder/builder.ml
Normal file
721
builder/builder.ml
Normal file
@@ -0,0 +1,721 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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
|
||||||
|
|
||||||
|
module G = Guestfs
|
||||||
|
|
||||||
|
open Common_utils
|
||||||
|
open Password
|
||||||
|
|
||||||
|
open Unix
|
||||||
|
open Printf
|
||||||
|
|
||||||
|
let quote = Filename.quote
|
||||||
|
|
||||||
|
(* Command line argument parsing. *)
|
||||||
|
let prog = Filename.basename Sys.executable_name
|
||||||
|
|
||||||
|
let cachedir =
|
||||||
|
try Some (Sys.getenv "XDG_CACHE_HOME" // "virt-builder")
|
||||||
|
with Not_found ->
|
||||||
|
try Some (Sys.getenv "HOME" // ".cache" // "virt-builder")
|
||||||
|
with Not_found ->
|
||||||
|
None (* no cache directory *)
|
||||||
|
|
||||||
|
let mode, arg,
|
||||||
|
attach, cache, check_signature, curl, debug, fingerprint,
|
||||||
|
firstboot, run,
|
||||||
|
format, gpg, install, list_long, network, output,
|
||||||
|
password_crypto, quiet, root_password,
|
||||||
|
size, source, upload =
|
||||||
|
let display_version () =
|
||||||
|
let g = new G.guestfs () in
|
||||||
|
let version = g#version () in
|
||||||
|
printf (f_"virt-builder %Ld.%Ld.%Ld%s\n")
|
||||||
|
version.G.major version.G.minor version.G.release version.G.extra;
|
||||||
|
exit 0
|
||||||
|
in
|
||||||
|
|
||||||
|
let mode = ref `Install in
|
||||||
|
let list_mode () = mode := `List in
|
||||||
|
let get_kernel_mode () = mode := `Get_kernel in
|
||||||
|
let delete_cache_mode () = mode := `Delete_cache in
|
||||||
|
|
||||||
|
let attach = ref [] in
|
||||||
|
let attach_format = ref None in
|
||||||
|
let set_attach_format = function
|
||||||
|
| "auto" -> attach_format := None
|
||||||
|
| s -> attach_format := Some s
|
||||||
|
in
|
||||||
|
let attach_disk s = attach := (!attach_format, s) :: !attach in
|
||||||
|
|
||||||
|
let cache = ref cachedir in
|
||||||
|
let set_cache arg = cache := Some arg in
|
||||||
|
let no_cache () = cache := None in
|
||||||
|
|
||||||
|
let check_signature = ref true in
|
||||||
|
let curl = ref "curl" in
|
||||||
|
let debug = ref false in
|
||||||
|
|
||||||
|
let fingerprint =
|
||||||
|
try Some (Sys.getenv "VIRT_BUILDER_FINGERPRINT")
|
||||||
|
with Not_found -> None in
|
||||||
|
let fingerprint = ref fingerprint in
|
||||||
|
let set_fingerprint fp = fingerprint := Some fp in
|
||||||
|
|
||||||
|
let firstboot = ref [] in
|
||||||
|
let add_firstboot s =
|
||||||
|
if not (Sys.file_exists s) then (
|
||||||
|
eprintf (f_"%s: --firstboot: %s: file not found.\n") prog s;
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
firstboot := `Script s :: !firstboot
|
||||||
|
in
|
||||||
|
let add_firstboot_cmd s = firstboot := `Command s :: !firstboot in
|
||||||
|
let add_firstboot_install pkgs =
|
||||||
|
let pkgs = string_nsplit "," pkgs in
|
||||||
|
firstboot := `Packages pkgs :: !firstboot
|
||||||
|
in
|
||||||
|
|
||||||
|
let format = ref "" in
|
||||||
|
let gpg = ref "gpg" in
|
||||||
|
|
||||||
|
let install = ref [] in
|
||||||
|
let add_install pkgs =
|
||||||
|
let pkgs = string_nsplit "," pkgs in
|
||||||
|
install := pkgs @ !install
|
||||||
|
in
|
||||||
|
|
||||||
|
let list_long = ref false in
|
||||||
|
let network = ref true in
|
||||||
|
let output = ref "" in
|
||||||
|
|
||||||
|
let password_crypto : password_crypto option ref = ref None in
|
||||||
|
let set_password_crypto arg =
|
||||||
|
password_crypto := Some (password_crypto_of_string ~prog arg)
|
||||||
|
in
|
||||||
|
|
||||||
|
let quiet = ref false in
|
||||||
|
|
||||||
|
let root_password = ref None in
|
||||||
|
let set_root_password arg =
|
||||||
|
let pw = get_password ~prog arg in
|
||||||
|
root_password := Some pw
|
||||||
|
in
|
||||||
|
|
||||||
|
let run = ref [] in
|
||||||
|
let add_run s =
|
||||||
|
if not (Sys.file_exists s) then (
|
||||||
|
eprintf (f_"%s: --run: %s: file not found.\n") prog s;
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
run := `Script s :: !run
|
||||||
|
in
|
||||||
|
let add_run_cmd s = run := `Command s :: !run in
|
||||||
|
|
||||||
|
let size = ref None in
|
||||||
|
let set_size arg = size := Some (parse_size ~prog arg) in
|
||||||
|
|
||||||
|
let source =
|
||||||
|
try Sys.getenv "VIRT_BUILDER_SOURCE"
|
||||||
|
with Not_found -> "http://libguestfs.org/download/builder/index.asc" in
|
||||||
|
let source = ref source in
|
||||||
|
|
||||||
|
let upload = ref [] in
|
||||||
|
let add_upload arg =
|
||||||
|
let i =
|
||||||
|
try String.index arg ':'
|
||||||
|
with Not_found ->
|
||||||
|
eprintf (f_"%s: invalid --upload format, see the man page.\n") prog;
|
||||||
|
exit 1 in
|
||||||
|
let len = String.length arg in
|
||||||
|
let file = String.sub arg 0 i in
|
||||||
|
if not (Sys.file_exists file) then (
|
||||||
|
eprintf (f_"%s: --upload: %s: file not found.\n") prog file;
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
let dest = String.sub arg (i+1) (len-(i+1)) in
|
||||||
|
upload := (file, dest) :: !upload
|
||||||
|
in
|
||||||
|
|
||||||
|
let ditto = " -\"-" in
|
||||||
|
let argspec = Arg.align [
|
||||||
|
"--attach", Arg.String attach_disk, "iso" ^ " " ^ s_"Attach data disk/ISO during install";
|
||||||
|
"--attach-format", Arg.String set_attach_format,
|
||||||
|
"format" ^ " " ^ s_"Set attach disk format";
|
||||||
|
"--cache", Arg.String set_cache, "dir" ^ " " ^ s_"Set template cache dir";
|
||||||
|
"--no-cache", Arg.Unit no_cache, " " ^ s_"Disable template cache";
|
||||||
|
"--check-signature", Arg.Set check_signature,
|
||||||
|
" " ^ s_"Check digital signatures";
|
||||||
|
"--check-signatures", Arg.Set check_signature, ditto;
|
||||||
|
"--no-check-signature", Arg.Clear check_signature,
|
||||||
|
" " ^ s_"Disable digital signatures";
|
||||||
|
"--no-check-signatures", Arg.Clear check_signature, ditto;
|
||||||
|
"--curl", Arg.Set_string curl, "curl" ^ " " ^ s_"Set curl binary/command";
|
||||||
|
"--delete-cache", Arg.Unit delete_cache_mode,
|
||||||
|
" " ^ s_"Delete the template cache";
|
||||||
|
"--fingerprint", Arg.String set_fingerprint,
|
||||||
|
"AAAA.." ^ " " ^ s_"Fingerprint of valid signing key";
|
||||||
|
"--firstboot", Arg.String add_firstboot, "script" ^ " " ^ s_"Run script at first guest boot";
|
||||||
|
"--firstboot-command", Arg.String add_firstboot_cmd, "cmd+args" ^ " " ^ s_"Run command at first guest boot";
|
||||||
|
"--firstboot-install", Arg.String add_firstboot_install,
|
||||||
|
"pkg,pkg" ^ " " ^ s_"Add package(s) to install at firstboot";
|
||||||
|
"--format", Arg.Set_string format, "raw|qcow2" ^ " " ^ s_"Output format (default: raw)";
|
||||||
|
"--get-kernel", Arg.Unit get_kernel_mode,
|
||||||
|
"image" ^ " " ^ s_"Get kernel from image";
|
||||||
|
"--gpg", Arg.Set_string gpg, "gpg" ^ " " ^ s_"Set GPG binary/command";
|
||||||
|
"--install", Arg.String add_install, "pkg,pkg" ^ " " ^ s_"Add package(s) to install";
|
||||||
|
"-l", Arg.Unit list_mode, " " ^ s_"List available templates";
|
||||||
|
"--list", Arg.Unit list_mode, ditto;
|
||||||
|
"--long", Arg.Set list_long, ditto;
|
||||||
|
"--long-options", Arg.Unit display_long_options, " " ^ s_"List long options";
|
||||||
|
"--network", Arg.Set network, " " ^ s_"Enable appliance network (default)";
|
||||||
|
"--no-network", Arg.Clear network, " " ^ s_"Disable appliance network";
|
||||||
|
"-o", Arg.Set_string output, "file" ^ " " ^ s_"Set output filename";
|
||||||
|
"--output", Arg.Set_string output, "file" ^ ditto;
|
||||||
|
"--password-crypto", Arg.String set_password_crypto,
|
||||||
|
"md5|sha256|sha512" ^ " " ^ s_"Set password crypto";
|
||||||
|
"--quiet", Arg.Set quiet, " " ^ s_"No progress messages";
|
||||||
|
"--root-password", Arg.String set_root_password,
|
||||||
|
"..." ^ " " ^ s_"Set root password";
|
||||||
|
"--run", Arg.String add_run, "script" ^ " " ^ s_"Run script in disk image";
|
||||||
|
"--run-command", Arg.String add_run_cmd, "cmd+args" ^ " " ^ s_"Run command in disk image";
|
||||||
|
"--size", Arg.String set_size, "size" ^ " " ^ s_"Set output disk size";
|
||||||
|
"--source", Arg.Set_string source, "URL" ^ " " ^ s_"Set source URL";
|
||||||
|
"--upload", Arg.String add_upload, "file:dest" ^ " " ^ s_"Upload file to dest";
|
||||||
|
"-v", Arg.Set debug, " " ^ s_"Enable debugging messages";
|
||||||
|
"--verbose", Arg.Set debug, ditto;
|
||||||
|
"-V", Arg.Unit display_version, " " ^ s_"Display version and exit";
|
||||||
|
"--version", Arg.Unit display_version, ditto;
|
||||||
|
] in
|
||||||
|
long_options := argspec;
|
||||||
|
|
||||||
|
let args = ref [] in
|
||||||
|
let anon_fun s = args := s :: !args in
|
||||||
|
let usage_msg =
|
||||||
|
sprintf (f_"\
|
||||||
|
%s: build virtual machine images quickly
|
||||||
|
|
||||||
|
A short summary of the options is given below. For detailed help please
|
||||||
|
read the man page virt-builder(1).
|
||||||
|
")
|
||||||
|
prog in
|
||||||
|
Arg.parse argspec anon_fun usage_msg;
|
||||||
|
|
||||||
|
(* Dereference options. *)
|
||||||
|
let args = List.rev !args in
|
||||||
|
let mode = !mode in
|
||||||
|
let attach = List.rev !attach in
|
||||||
|
let cache = !cache in
|
||||||
|
let check_signature = !check_signature in
|
||||||
|
let curl = !curl in
|
||||||
|
let debug = !debug in
|
||||||
|
let fingerprint = !fingerprint in
|
||||||
|
let firstboot = List.rev !firstboot in
|
||||||
|
let run = List.rev !run in
|
||||||
|
let format = match !format with "" -> None | s -> Some s in
|
||||||
|
let gpg = !gpg in
|
||||||
|
let install = !install in
|
||||||
|
let list_long = !list_long in
|
||||||
|
let network = !network in
|
||||||
|
let output = match !output with "" -> None | s -> Some s in
|
||||||
|
let password_crypto = !password_crypto in
|
||||||
|
let quiet = !quiet in
|
||||||
|
let root_password = !root_password in
|
||||||
|
let size = !size in
|
||||||
|
let source = !source in
|
||||||
|
let upload = List.rev !upload in
|
||||||
|
|
||||||
|
(* Check options. *)
|
||||||
|
let arg =
|
||||||
|
match mode with
|
||||||
|
| `Install ->
|
||||||
|
(match args with
|
||||||
|
| [arg] -> arg
|
||||||
|
| [] ->
|
||||||
|
eprintf (f_"%s: virt-builder os-version\nMissing 'os-version'. Use '--list' to list available template names.\n") prog;
|
||||||
|
exit 1
|
||||||
|
| _ ->
|
||||||
|
eprintf (f_"%s: virt-builder: too many parameters, expecting 'os-version'\n") prog;
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
| `List ->
|
||||||
|
(match args with
|
||||||
|
| [] -> ""
|
||||||
|
| _ ->
|
||||||
|
eprintf (f_"%s: virt-builder --list does not need any extra arguments.\n") prog;
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
| `Delete_cache ->
|
||||||
|
(match args with
|
||||||
|
| [] -> ""
|
||||||
|
| _ ->
|
||||||
|
eprintf (f_"%s: virt-builder --delete-cache does not need any extra arguments.\n") prog;
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
| `Get_kernel ->
|
||||||
|
(match args with
|
||||||
|
| [arg] -> arg
|
||||||
|
| [] ->
|
||||||
|
eprintf (f_"%s: virt-builder --get-kernel image\nMissing 'image' (disk image file) argument.\n") prog;
|
||||||
|
exit 1
|
||||||
|
| _ ->
|
||||||
|
eprintf (f_"%s: virt-builder --get-kernel: too many parameters\n") prog;
|
||||||
|
exit 1
|
||||||
|
) in
|
||||||
|
|
||||||
|
mode, arg,
|
||||||
|
attach, cache, check_signature, curl, debug, fingerprint,
|
||||||
|
firstboot, run,
|
||||||
|
format, gpg, install, list_long, network, output,
|
||||||
|
password_crypto, quiet, root_password,
|
||||||
|
size, source, upload
|
||||||
|
|
||||||
|
(* Timestamped messages in ordinary, non-debug non-quiet mode. *)
|
||||||
|
let msg fs = make_message_function ~quiet fs
|
||||||
|
|
||||||
|
(* If debugging, echo the command line arguments. *)
|
||||||
|
let () =
|
||||||
|
if debug then (
|
||||||
|
eprintf "command line:";
|
||||||
|
List.iter (eprintf " %s") (Array.to_list Sys.argv);
|
||||||
|
prerr_newline ()
|
||||||
|
)
|
||||||
|
|
||||||
|
(* --get-kernel is really a different program ... *)
|
||||||
|
let () =
|
||||||
|
if mode = `Get_kernel then (
|
||||||
|
Get_kernel.get_kernel ~debug ?format ?output arg;
|
||||||
|
exit 0
|
||||||
|
)
|
||||||
|
|
||||||
|
let () =
|
||||||
|
if mode = `Delete_cache then (
|
||||||
|
match cachedir with
|
||||||
|
| Some cachedir ->
|
||||||
|
msg "Deleting: %s" cachedir;
|
||||||
|
let cmd = sprintf "rm -rf %s" (quote cachedir) in
|
||||||
|
ignore (Sys.command cmd);
|
||||||
|
exit 0
|
||||||
|
| None ->
|
||||||
|
eprintf (f_"%s: error: could not find cache directory\nIs $HOME set?\n")
|
||||||
|
prog;
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
|
||||||
|
(* Check various programs/dependencies are installed. *)
|
||||||
|
let have_nbdkit =
|
||||||
|
(* Check that gpg is installed. Optional as long as the user
|
||||||
|
* disables all signature checks.
|
||||||
|
*)
|
||||||
|
let cmd = sprintf "%s --help >/dev/null 2>&1" gpg in
|
||||||
|
if Sys.command cmd <> 0 then (
|
||||||
|
if check_signature then (
|
||||||
|
eprintf (f_"%s: gpg is not installed (or does not work)\nYou should install gpg, or use --gpg option, or use --no-check-signature.\n") prog;
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
else if debug then
|
||||||
|
eprintf (f_"%s: warning: gpg program is not available\n") prog
|
||||||
|
);
|
||||||
|
|
||||||
|
(* Check that curl works. *)
|
||||||
|
let cmd = sprintf "%s --help >/dev/null 2>&1" curl in
|
||||||
|
if Sys.command cmd <> 0 then (
|
||||||
|
eprintf (f_"%s: curl is not installed (or does not work)\n") prog;
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
|
||||||
|
(* Check that virt-resize works. *)
|
||||||
|
let cmd = "virt-resize --help >/dev/null 2>&1" in
|
||||||
|
if Sys.command cmd <> 0 then (
|
||||||
|
eprintf (f_"%s: virt-resize is not installed (or does not work)\n") prog;
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
|
||||||
|
(* Find out if nbdkit + nbdkit-xz-plugin is installed (optional). *)
|
||||||
|
let cmd =
|
||||||
|
sprintf "nbdkit %s/nbdkit/plugins/nbdkit-xz-plugin.so --help >/dev/null 2>&1"
|
||||||
|
Libdir.libdir in
|
||||||
|
let have_nbdkit = Sys.command cmd = 0 in
|
||||||
|
if not have_nbdkit && debug then
|
||||||
|
eprintf (f_"%s: warning: nbdkit or nbdkit-xz-plugin is not available\n")
|
||||||
|
prog;
|
||||||
|
|
||||||
|
have_nbdkit
|
||||||
|
|
||||||
|
(* Create the cache directory. *)
|
||||||
|
let cache =
|
||||||
|
match cache with
|
||||||
|
| None -> None
|
||||||
|
| (Some dir) as cache ->
|
||||||
|
(try mkdir dir 0o755 with _ -> ());
|
||||||
|
if Sys.is_directory dir then cache else None
|
||||||
|
|
||||||
|
(* Make the downloader and signature checker abstract data types. *)
|
||||||
|
let downloader =
|
||||||
|
Downloader.create ~debug ~curl ~cache
|
||||||
|
let sigchecker =
|
||||||
|
Sigchecker.create ~debug ~gpg ?fingerprint ~check_signature
|
||||||
|
|
||||||
|
(* Download the source (index) file. *)
|
||||||
|
let index =
|
||||||
|
Index_parser.get_index ~debug ~downloader ~sigchecker source
|
||||||
|
|
||||||
|
(* Now we can do the --list option. *)
|
||||||
|
let () =
|
||||||
|
if mode = `List then (
|
||||||
|
List_entries.list_entries ~list_long ~source index;
|
||||||
|
exit 0
|
||||||
|
)
|
||||||
|
|
||||||
|
(* If we get here, we want to create a guest (but which one?) *)
|
||||||
|
let entry =
|
||||||
|
assert (mode = `Install);
|
||||||
|
|
||||||
|
try List.assoc arg index
|
||||||
|
with Not_found ->
|
||||||
|
eprintf (f_"%s: cannot find os-version '%s'.\nUse --list to list available guest types.\n")
|
||||||
|
prog arg;
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
(* Download the template, or it may be in the cache. *)
|
||||||
|
let template =
|
||||||
|
let template, delete_on_exit =
|
||||||
|
let { Index_parser.revision = revision; file_uri = file_uri } = entry in
|
||||||
|
let template = arg, revision in
|
||||||
|
msg (f_"Downloading: %s") file_uri;
|
||||||
|
Downloader.download downloader ~template file_uri in
|
||||||
|
if delete_on_exit then unlink_on_exit template;
|
||||||
|
template
|
||||||
|
|
||||||
|
(* Check the signature of the file. *)
|
||||||
|
let () =
|
||||||
|
let sigfile =
|
||||||
|
match entry with
|
||||||
|
| { Index_parser.signature_uri = None } -> None
|
||||||
|
| { Index_parser.signature_uri = Some signature_uri } ->
|
||||||
|
let sigfile, delete_on_exit =
|
||||||
|
Downloader.download downloader signature_uri in
|
||||||
|
if delete_on_exit then unlink_on_exit sigfile;
|
||||||
|
Some sigfile in
|
||||||
|
|
||||||
|
Sigchecker.verify_detached sigchecker template sigfile
|
||||||
|
|
||||||
|
(* Check the --size option. *)
|
||||||
|
let headroom = 256L *^ 1024L *^ 1024L
|
||||||
|
let size =
|
||||||
|
let { Index_parser.size = default_size } = entry in
|
||||||
|
match size with
|
||||||
|
| None -> default_size +^ headroom
|
||||||
|
| Some size ->
|
||||||
|
if size < default_size +^ headroom then (
|
||||||
|
eprintf (f_"%s: --size is too small for this disk image, minimum size is %s\n")
|
||||||
|
prog (human_size default_size);
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
size
|
||||||
|
|
||||||
|
(* Create the output file. *)
|
||||||
|
let output, format =
|
||||||
|
match output, format with
|
||||||
|
| None, None -> sprintf "%s.img" arg, "raw"
|
||||||
|
| None, Some "raw" -> sprintf "%s.img" arg, "raw"
|
||||||
|
| None, Some format -> sprintf "%s.%s" arg format, format
|
||||||
|
| Some output, None -> output, "raw"
|
||||||
|
| Some output, Some format -> output, format
|
||||||
|
|
||||||
|
let delete_output_file =
|
||||||
|
let cmd =
|
||||||
|
sprintf "qemu-img create -f %s %s %Ld%s"
|
||||||
|
(quote format) (quote output) size
|
||||||
|
(if debug then "" else " >/dev/null 2>&1") in
|
||||||
|
let r = Sys.command cmd in
|
||||||
|
if r <> 0 then (
|
||||||
|
eprintf (f_"%s: error: could not create output file '%s'\n") prog output;
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
(* This ensures the output file will be deleted on failure,
|
||||||
|
* until we set !delete_output_file = false at the end of the build.
|
||||||
|
*)
|
||||||
|
let delete_output_file = ref true in
|
||||||
|
let delete_file () =
|
||||||
|
if !delete_output_file then
|
||||||
|
try unlink output with _ -> ()
|
||||||
|
in
|
||||||
|
at_exit delete_file;
|
||||||
|
delete_output_file
|
||||||
|
|
||||||
|
let source =
|
||||||
|
(* XXX Disable this for now because libvirt is broken:
|
||||||
|
* https://bugzilla.redhat.com/show_bug.cgi?id=1011063
|
||||||
|
*)
|
||||||
|
if have_nbdkit && false then (
|
||||||
|
(* If we have nbdkit, then we can use NBD to uncompress the xz
|
||||||
|
* file on the fly.
|
||||||
|
*)
|
||||||
|
let socket = Filename.temp_file "vbnbd" ".sock" in
|
||||||
|
let source = sprintf "nbd://?socket=%s" socket in
|
||||||
|
let argv = [| "nbdkit"; "-r"; "-f"; "-U"; socket;
|
||||||
|
Libdir.libdir // "nbdkit/plugins/nbdkit-xz-plugin.so";
|
||||||
|
"file=" ^ template |] in
|
||||||
|
let pid =
|
||||||
|
match fork () with
|
||||||
|
| 0 -> (* child *)
|
||||||
|
execvp "nbdkit" argv
|
||||||
|
| pid -> pid in
|
||||||
|
(* Clean up when the program exits. *)
|
||||||
|
let clean_up () =
|
||||||
|
(try kill pid Sys.sigterm with _ -> ());
|
||||||
|
(try unlink socket with _ -> ())
|
||||||
|
in
|
||||||
|
at_exit clean_up;
|
||||||
|
source
|
||||||
|
)
|
||||||
|
else (
|
||||||
|
(* Otherwise we have to uncompress it to a temporary file. *)
|
||||||
|
let { Index_parser.file_uri = file_uri } = entry in
|
||||||
|
let tmpfile = Filename.temp_file "vbsrc" ".img" in
|
||||||
|
let cmd = sprintf "xzcat %s > %s" (quote template) (quote tmpfile) in
|
||||||
|
if debug then eprintf "%s\n%!" cmd;
|
||||||
|
msg (f_"Uncompressing: %s") file_uri;
|
||||||
|
let r = Sys.command cmd in
|
||||||
|
if r <> 0 then (
|
||||||
|
eprintf (f_"%s: error: failed to uncompress template\n") prog;
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
unlink_on_exit tmpfile;
|
||||||
|
tmpfile
|
||||||
|
)
|
||||||
|
|
||||||
|
(* Resize the source to the output file. *)
|
||||||
|
let () =
|
||||||
|
msg (f_"Running virt-resize to expand the disk to %s") (human_size size);
|
||||||
|
|
||||||
|
let { Index_parser.expand = expand; lvexpand = lvexpand; format = format } =
|
||||||
|
entry in
|
||||||
|
let cmd =
|
||||||
|
sprintf "virt-resize%s%s%s%s %s %s"
|
||||||
|
(if debug then " --verbose" else " --quiet")
|
||||||
|
(match format with
|
||||||
|
| None -> ""
|
||||||
|
| Some format -> sprintf " --format %s" (quote format))
|
||||||
|
(match expand with
|
||||||
|
| None -> ""
|
||||||
|
| Some expand -> sprintf " --expand %s" (quote expand))
|
||||||
|
(match lvexpand with
|
||||||
|
| None -> ""
|
||||||
|
| Some lvexpand -> sprintf " --lv-expand %s" (quote lvexpand))
|
||||||
|
(quote source) (quote output) in
|
||||||
|
if debug then eprintf "%s\n%!" cmd;
|
||||||
|
let r = Sys.command cmd in
|
||||||
|
if r <> 0 then (
|
||||||
|
eprintf (f_"%s: error: virt-resize failed\n") prog;
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
|
||||||
|
(* Now mount the output disk so we can make changes. *)
|
||||||
|
let g =
|
||||||
|
msg (f_"Opening the new disk");
|
||||||
|
|
||||||
|
let g = new G.guestfs () in
|
||||||
|
if debug then g#set_trace true;
|
||||||
|
|
||||||
|
g#set_network network;
|
||||||
|
|
||||||
|
g#add_drive_opts ~format output;
|
||||||
|
|
||||||
|
(* Attach ISOs, if we have any. *)
|
||||||
|
List.iter (
|
||||||
|
fun (format, file) ->
|
||||||
|
g#add_drive_opts ?format ~readonly:true file;
|
||||||
|
) attach;
|
||||||
|
|
||||||
|
g#launch ();
|
||||||
|
|
||||||
|
g
|
||||||
|
|
||||||
|
(* Inspect the disk and mount it up. *)
|
||||||
|
let root =
|
||||||
|
match Array.to_list (g#inspect_os ()) with
|
||||||
|
| [root] ->
|
||||||
|
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: %s (ignored)\n") prog msg
|
||||||
|
) mps;
|
||||||
|
root
|
||||||
|
| _ ->
|
||||||
|
eprintf (f_"%s: no guest operating systems or multiboot OS found in this disk image\nThis is a failure of the source repository. Use -v for more information.\n") prog;
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
(* Set the random seed. *)
|
||||||
|
let () = ignore (Random_seed.set_random_seed g root)
|
||||||
|
|
||||||
|
(* Useful wrapper for scripts. *)
|
||||||
|
let do_run cmd =
|
||||||
|
(* Add a prologue to the scripts:
|
||||||
|
* - Pass environment variables through from the host.
|
||||||
|
* - Send stdout to stderr so we capture all output in error messages.
|
||||||
|
*)
|
||||||
|
let env_vars =
|
||||||
|
filter_map (
|
||||||
|
fun name ->
|
||||||
|
try Some (sprintf "export %s=%s" name (quote (Sys.getenv name)))
|
||||||
|
with Not_found -> None
|
||||||
|
) [ "http_proxy"; "https_proxy"; "ftp_proxy" ] in
|
||||||
|
let env_vars = String.concat "\n" env_vars ^ "\n" in
|
||||||
|
|
||||||
|
let cmd = sprintf "\
|
||||||
|
exec 1>&2
|
||||||
|
%s
|
||||||
|
%s
|
||||||
|
" env_vars cmd in
|
||||||
|
|
||||||
|
if debug then eprintf "running: %s\n%!" cmd;
|
||||||
|
ignore (g#sh cmd)
|
||||||
|
|
||||||
|
let guest_install_command packages =
|
||||||
|
let quoted_args = String.concat " " (List.map quote packages) in
|
||||||
|
match g#inspect_get_package_management root with
|
||||||
|
| "apt" ->
|
||||||
|
sprintf "apt-get -y install %s" quoted_args
|
||||||
|
| "pisi" ->
|
||||||
|
sprintf "pisi it %s" quoted_args
|
||||||
|
| "pacman" ->
|
||||||
|
sprintf "pacman -S %s" quoted_args
|
||||||
|
| "urpmi" ->
|
||||||
|
sprintf "urpmi %s" quoted_args
|
||||||
|
| "yum" ->
|
||||||
|
sprintf "yum -y install %s" quoted_args
|
||||||
|
| "zypper" ->
|
||||||
|
(* XXX Should we use -n option? *)
|
||||||
|
sprintf "zypper in %s" quoted_args
|
||||||
|
| "unknown" ->
|
||||||
|
eprintf (f_"%s: --install is not supported for this guest operating system\n")
|
||||||
|
prog;
|
||||||
|
exit 1
|
||||||
|
| pm ->
|
||||||
|
eprintf (f_"%s: sorry, don't know how to use --install with the '%s' package manager\n")
|
||||||
|
prog pm;
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
(* Install packages. *)
|
||||||
|
let () =
|
||||||
|
if install <> [] then (
|
||||||
|
msg (f_"Installing packages: %s") (String.concat " " install);
|
||||||
|
|
||||||
|
let cmd = guest_install_command install in
|
||||||
|
do_run cmd;
|
||||||
|
)
|
||||||
|
|
||||||
|
(* Root password.
|
||||||
|
* Note 'None' means that we randomize the root password.
|
||||||
|
*)
|
||||||
|
let () =
|
||||||
|
let make_random_password () =
|
||||||
|
(* Get random characters from the set [A-Za-z0-9] *)
|
||||||
|
let chars =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" in
|
||||||
|
let nr_chars = String.length chars in
|
||||||
|
|
||||||
|
let chan = open_in "/dev/urandom" in
|
||||||
|
let buf = String.create 16 in
|
||||||
|
for i = 0 to 15 do
|
||||||
|
buf.[i] <- chars.[Char.code (input_char chan) mod nr_chars]
|
||||||
|
done;
|
||||||
|
close_in chan;
|
||||||
|
|
||||||
|
msg "Random root password: %s [did you mean to use --root-password?]" buf;
|
||||||
|
|
||||||
|
buf
|
||||||
|
in
|
||||||
|
|
||||||
|
let root_password =
|
||||||
|
match root_password with
|
||||||
|
| Some pw -> pw
|
||||||
|
| None -> make_random_password () in
|
||||||
|
|
||||||
|
match g#inspect_get_type root with
|
||||||
|
| "linux" ->
|
||||||
|
let h = Hashtbl.create 1 in
|
||||||
|
Hashtbl.replace h "root" root_password;
|
||||||
|
set_linux_passwords ~prog ?password_crypto g root h
|
||||||
|
| _ ->
|
||||||
|
()
|
||||||
|
|
||||||
|
(* Upload files. *)
|
||||||
|
let () =
|
||||||
|
List.iter (
|
||||||
|
fun (file, dest) ->
|
||||||
|
msg (f_"Uploading: %s") dest;
|
||||||
|
g#upload file dest
|
||||||
|
) upload
|
||||||
|
|
||||||
|
(* Firstboot scripts/commands/install. *)
|
||||||
|
let () =
|
||||||
|
let id = ref 0 in
|
||||||
|
List.iter (
|
||||||
|
fun op ->
|
||||||
|
incr id;
|
||||||
|
let id = sprintf "%03d" !id in
|
||||||
|
match op with
|
||||||
|
| `Script script ->
|
||||||
|
msg (f_"Installing firstboot script: [%s] %s") id script;
|
||||||
|
let cmd = read_whole_file script in
|
||||||
|
Firstboot.add_firstboot_script g root id cmd
|
||||||
|
| `Command cmd ->
|
||||||
|
msg (f_"Installing firstboot command: [%s] %s") id cmd;
|
||||||
|
Firstboot.add_firstboot_script g root id cmd
|
||||||
|
| `Packages pkgs ->
|
||||||
|
msg (f_"Installing firstboot packages: [%s] %s") id
|
||||||
|
(String.concat " " pkgs);
|
||||||
|
let cmd = guest_install_command pkgs in
|
||||||
|
Firstboot.add_firstboot_script g root id cmd
|
||||||
|
) firstboot
|
||||||
|
|
||||||
|
(* Run scripts. *)
|
||||||
|
let () =
|
||||||
|
List.iter (
|
||||||
|
function
|
||||||
|
| `Script script ->
|
||||||
|
msg (f_"Running: %s") script;
|
||||||
|
let cmd = read_whole_file script in
|
||||||
|
do_run cmd
|
||||||
|
| `Command cmd ->
|
||||||
|
msg (f_"Running: %s") cmd;
|
||||||
|
do_run cmd
|
||||||
|
) run
|
||||||
|
|
||||||
|
(* Unmount everything and we're done! *)
|
||||||
|
let () =
|
||||||
|
msg "Finishing off";
|
||||||
|
|
||||||
|
g#umount_all ();
|
||||||
|
g#shutdown ();
|
||||||
|
g#close ()
|
||||||
|
|
||||||
|
(* Now that we've finished the build, don't delete the output file on
|
||||||
|
* exit.
|
||||||
|
*)
|
||||||
|
let () =
|
||||||
|
delete_output_file := false
|
||||||
114
builder/downloader.ml
Normal file
114
builder/downloader.ml
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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 Unix
|
||||||
|
open Printf
|
||||||
|
|
||||||
|
let quote = Filename.quote
|
||||||
|
let (//) = Filename.concat
|
||||||
|
|
||||||
|
type uri = string
|
||||||
|
type filename = string
|
||||||
|
|
||||||
|
type t = {
|
||||||
|
debug : bool;
|
||||||
|
curl : string;
|
||||||
|
cache : string option; (* cache directory for templates *)
|
||||||
|
}
|
||||||
|
|
||||||
|
let create ~debug ~curl ~cache = {
|
||||||
|
debug = debug;
|
||||||
|
curl = curl;
|
||||||
|
cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rec download t ?template uri =
|
||||||
|
match template with
|
||||||
|
| None -> (* no cache, simple download *)
|
||||||
|
(* Create a temporary name. *)
|
||||||
|
let tmpfile = Filename.temp_file "vbcache" ".txt" in
|
||||||
|
download_to t uri tmpfile;
|
||||||
|
(tmpfile, true)
|
||||||
|
|
||||||
|
| Some (name, revision) ->
|
||||||
|
match t.cache with
|
||||||
|
| None ->
|
||||||
|
(* Not using the cache at all? *)
|
||||||
|
download t uri
|
||||||
|
|
||||||
|
| Some cachedir ->
|
||||||
|
let filename = cachedir // sprintf "%s.%d" name revision in
|
||||||
|
|
||||||
|
(* Is the requested template name + revision in the cache already?
|
||||||
|
* If not, download it.
|
||||||
|
*)
|
||||||
|
if not (Sys.file_exists filename) then
|
||||||
|
download_to t uri filename;
|
||||||
|
|
||||||
|
(filename, false)
|
||||||
|
|
||||||
|
and download_to t uri filename =
|
||||||
|
(* Get the status code first to ensure the file exists. *)
|
||||||
|
let cmd = sprintf "%s%s -g -o /dev/null -I -w '%%{http_code}' %s"
|
||||||
|
t.curl (if t.debug then "" else " -s -S") (quote uri) in
|
||||||
|
let chan = open_process_in cmd in
|
||||||
|
let status_code = input_line chan in
|
||||||
|
let stat = close_process_in chan in
|
||||||
|
(match stat with
|
||||||
|
| WEXITED 0 -> ()
|
||||||
|
| WEXITED i ->
|
||||||
|
eprintf (f_"virt-builder: curl (download) command failed downloading '%s'\n") uri;
|
||||||
|
exit 1
|
||||||
|
| WSIGNALED i ->
|
||||||
|
eprintf (f_"virt-builder: external command '%s' killed by signal %d\n")
|
||||||
|
cmd i;
|
||||||
|
exit 1
|
||||||
|
| WSTOPPED i ->
|
||||||
|
eprintf (f_"virt-builder: external command '%s' stopped by signal %d\n")
|
||||||
|
cmd i;
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
let bad_status_code = function
|
||||||
|
| "" -> true
|
||||||
|
| s when s.[0] = '4' -> true (* 4xx *)
|
||||||
|
| s when s.[0] = '5' -> true (* 5xx *)
|
||||||
|
| _ -> false
|
||||||
|
in
|
||||||
|
if bad_status_code status_code then (
|
||||||
|
eprintf (f_"virt-builder: failed to download %s: HTTP status code %s\n")
|
||||||
|
uri status_code;
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
|
||||||
|
(* Now download the file. *)
|
||||||
|
let filename_new = filename ^ ".new" in
|
||||||
|
let cmd = sprintf "%s%s -g -o %s %s"
|
||||||
|
t.curl (if t.debug then "" else " -s -S")
|
||||||
|
(quote filename_new) (quote uri) in
|
||||||
|
if t.debug then eprintf "%s\n%!" cmd;
|
||||||
|
let r = Sys.command cmd in
|
||||||
|
if r <> 0 then (
|
||||||
|
eprintf (f_"virt-builder: curl (download) command failed downloading '%s'\n") uri;
|
||||||
|
(try unlink filename_new with _ -> ());
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
|
||||||
|
(* Rename the file if curl was successful. *)
|
||||||
|
rename filename_new filename
|
||||||
38
builder/downloader.mli
Normal file
38
builder/downloader.mli
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
(** This module is a wrapper around curl, plus local caching. *)
|
||||||
|
|
||||||
|
type uri = string
|
||||||
|
type filename = string
|
||||||
|
|
||||||
|
type t
|
||||||
|
(** The abstract data type. *)
|
||||||
|
|
||||||
|
val create : debug:bool -> curl:string -> cache:string option -> t
|
||||||
|
(** Create the abstract type. *)
|
||||||
|
|
||||||
|
val download : t -> ?template:(string*int) -> uri -> (filename * bool)
|
||||||
|
(** Download the URI, returning the downloaded filename and a
|
||||||
|
temporary file flag. The temporary file flag is [true] iff
|
||||||
|
the downloaded file is temporary and should be deleted by the
|
||||||
|
caller (otherwise it's in the cache and you shouldn't delete it).
|
||||||
|
|
||||||
|
For templates, you must supply [~template:(name, revision)]. This
|
||||||
|
causes the cache to be used (if possible). Name and revision are
|
||||||
|
used for cache control (see the man page for details). *)
|
||||||
121
builder/get_kernel.ml
Normal file
121
builder/get_kernel.ml
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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
|
||||||
|
|
||||||
|
module G = Guestfs
|
||||||
|
|
||||||
|
open Printf
|
||||||
|
|
||||||
|
let rex_numbers = Str.regexp "^\\([0-9]+\\)\\(.*\\)$"
|
||||||
|
let rex_letters = Str.regexp_case_fold "^\\([a-z]+\\)\\(.*\\)$"
|
||||||
|
|
||||||
|
(* Originally:
|
||||||
|
* http://rwmj.wordpress.com/2013/09/13/get-kernel-and-initramfs-from-a-disk-image/
|
||||||
|
*)
|
||||||
|
let rec get_kernel ~debug ?format ?output disk =
|
||||||
|
let g = new G.guestfs () in
|
||||||
|
if debug then g#set_trace true;
|
||||||
|
g#add_drive_opts ?format ~readonly:true disk;
|
||||||
|
g#launch ();
|
||||||
|
|
||||||
|
let roots = g#inspect_os () in
|
||||||
|
if Array.length roots = 0 then (
|
||||||
|
eprintf (f_"virt-builder: get-kernel: no operating system found\n");
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
if Array.length roots > 1 then (
|
||||||
|
eprintf (f_"virt-builder: get-kernel: daual/mult-boot images are not supported by this tool\n");
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
let root = roots.(0) in
|
||||||
|
|
||||||
|
(* Mount up the disks. *)
|
||||||
|
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_ro dev mp
|
||||||
|
with Guestfs.Error msg -> eprintf "%s (ignored)\n" msg
|
||||||
|
) mps;
|
||||||
|
|
||||||
|
(* Get all kernels and initramfses. *)
|
||||||
|
let glob w = Array.to_list (g#glob_expand w) in
|
||||||
|
let kernels = glob "/boot/vmlinuz-*" in
|
||||||
|
let initrds = glob "/boot/initramfs-*" in
|
||||||
|
|
||||||
|
(* Old RHEL: *)
|
||||||
|
let initrds = if initrds <> [] then initrds else glob "/boot/initrd-*" in
|
||||||
|
|
||||||
|
(* Debian/Ubuntu: *)
|
||||||
|
let initrds = if initrds <> [] then initrds else glob "/boot/initrd.img-*" in
|
||||||
|
|
||||||
|
(* Sort by version to get the latest version as first element. *)
|
||||||
|
let kernels = List.rev (List.sort compare_version kernels) in
|
||||||
|
let initrds = List.rev (List.sort compare_version initrds) in
|
||||||
|
|
||||||
|
if kernels = [] then (
|
||||||
|
eprintf (f_"virt-builder: no kernel found\n");
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
|
||||||
|
(* Download the latest. *)
|
||||||
|
let outputdir =
|
||||||
|
match output with
|
||||||
|
| None -> Filename.current_dir_name
|
||||||
|
| Some dir -> dir in
|
||||||
|
let kernel_in = List.hd kernels in
|
||||||
|
let kernel_out = outputdir // Filename.basename kernel_in in
|
||||||
|
printf "download: %s -> %s\n%!" kernel_in kernel_out;
|
||||||
|
g#download kernel_in kernel_out;
|
||||||
|
|
||||||
|
if initrds <> [] then (
|
||||||
|
let initrd_in = List.hd initrds in
|
||||||
|
let initrd_out = outputdir // Filename.basename initrd_in in
|
||||||
|
printf "download: %s -> %s\n%!" initrd_in initrd_out;
|
||||||
|
g#download initrd_in initrd_out
|
||||||
|
);
|
||||||
|
|
||||||
|
(* Shutdown. *)
|
||||||
|
g#shutdown ();
|
||||||
|
g#close ()
|
||||||
|
|
||||||
|
and compare_version v1 v2 =
|
||||||
|
compare (split_version v1) (split_version v2)
|
||||||
|
|
||||||
|
and split_version = function
|
||||||
|
| "" -> []
|
||||||
|
| str ->
|
||||||
|
let first, rest =
|
||||||
|
if Str.string_match rex_numbers str 0 then (
|
||||||
|
let n = Str.matched_group 1 str in
|
||||||
|
let rest = Str.matched_group 2 str in
|
||||||
|
let n =
|
||||||
|
try `Number (int_of_string n)
|
||||||
|
with Failure "int_of_string" -> `String n in
|
||||||
|
n, rest
|
||||||
|
)
|
||||||
|
else if Str.string_match rex_letters str 0 then
|
||||||
|
`String (Str.matched_group 1 str), Str.matched_group 2 str
|
||||||
|
else (
|
||||||
|
let len = String.length str in
|
||||||
|
`Char str.[0], String.sub str 1 (len-1)
|
||||||
|
) in
|
||||||
|
first :: split_version rest
|
||||||
19
builder/get_kernel.mli
Normal file
19
builder/get_kernel.mli
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
val get_kernel : debug:bool -> ?format:string -> ?output:string -> string -> unit
|
||||||
373
builder/index_parser.ml
Normal file
373
builder/index_parser.ml
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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
|
||||||
|
open Unix
|
||||||
|
|
||||||
|
type index = (string * entry) list (* string = "os-version" *)
|
||||||
|
and entry = {
|
||||||
|
printable_name : string option; (* the name= field *)
|
||||||
|
osinfo : string option;
|
||||||
|
file_uri : string;
|
||||||
|
signature_uri : string option;
|
||||||
|
revision : int;
|
||||||
|
format : string option;
|
||||||
|
size : int64;
|
||||||
|
compressed_size : int64 option;
|
||||||
|
expand : string option;
|
||||||
|
lvexpand : string option;
|
||||||
|
notes : string option;
|
||||||
|
hidden : bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
let print_entry chan (name, { printable_name = printable_name;
|
||||||
|
file_uri = file_uri;
|
||||||
|
osinfo = osinfo;
|
||||||
|
signature_uri = signature_uri;
|
||||||
|
revision = revision;
|
||||||
|
format = format;
|
||||||
|
size = size;
|
||||||
|
compressed_size = compressed_size;
|
||||||
|
expand = expand;
|
||||||
|
lvexpand = lvexpand;
|
||||||
|
notes = notes;
|
||||||
|
hidden = hidden }) =
|
||||||
|
let fp fs = fprintf chan fs in
|
||||||
|
fp "[%s]\n" name;
|
||||||
|
(match printable_name with
|
||||||
|
| None -> ()
|
||||||
|
| Some name -> fp "name=%s\n" name
|
||||||
|
);
|
||||||
|
(match osinfo with
|
||||||
|
| None -> ()
|
||||||
|
| Some id -> fp "osinfo=%s\n" id
|
||||||
|
);
|
||||||
|
fp "file=%s\n" file_uri;
|
||||||
|
(match signature_uri with
|
||||||
|
| None -> ()
|
||||||
|
| Some uri -> fp "sig=%s\n" uri
|
||||||
|
);
|
||||||
|
fp "revision=%d\n" revision;
|
||||||
|
(match format with
|
||||||
|
| None -> ()
|
||||||
|
| Some format -> fp "format=%s\n" format
|
||||||
|
);
|
||||||
|
fp "size=%Ld\n" size;
|
||||||
|
(match compressed_size with
|
||||||
|
| None -> ()
|
||||||
|
| Some size -> fp "compressed_size=%Ld\n" size
|
||||||
|
);
|
||||||
|
(match expand with
|
||||||
|
| None -> ()
|
||||||
|
| Some expand -> fp "expand=%s\n" expand
|
||||||
|
);
|
||||||
|
(match lvexpand with
|
||||||
|
| None -> ()
|
||||||
|
| Some lvexpand -> fp "lvexpand=%s\n" lvexpand
|
||||||
|
);
|
||||||
|
(match notes with
|
||||||
|
| None -> ()
|
||||||
|
| Some notes -> fp "notes=%s\n" notes
|
||||||
|
);
|
||||||
|
if hidden then fp "hidden=true\n"
|
||||||
|
|
||||||
|
let fieldname_rex = Str.regexp "^\\([a-z_]+\\)=\\(.*\\)$"
|
||||||
|
|
||||||
|
let get_index ~debug ~downloader ~sigchecker source =
|
||||||
|
let rec corrupt_line line =
|
||||||
|
eprintf (f_"virt-builder: error parsing index near this line:\n\n%s\n")
|
||||||
|
line;
|
||||||
|
corrupt_file ()
|
||||||
|
and corrupt_file () =
|
||||||
|
eprintf (f_"\nThe index file downloaded from '%s' is corrupt.\nYou need to ask the supplier of this file to fix it and upload a fixed version.\n")
|
||||||
|
source;
|
||||||
|
exit 1
|
||||||
|
in
|
||||||
|
|
||||||
|
let rec get_index () =
|
||||||
|
(* Get the index page. *)
|
||||||
|
let tmpfile, delete_tmpfile = Downloader.download downloader source in
|
||||||
|
|
||||||
|
(* Check index file signature (also verifies it was fully
|
||||||
|
* downloaded and not corrupted in transit).
|
||||||
|
*)
|
||||||
|
Sigchecker.verify sigchecker tmpfile;
|
||||||
|
|
||||||
|
(* Check the index page is not too huge. *)
|
||||||
|
let st = stat tmpfile in
|
||||||
|
if st.st_size > 1_000_000 then (
|
||||||
|
eprintf (f_"virt-builder: index page '%s' is too large (size %d bytes)\n")
|
||||||
|
source st.st_size;
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
|
||||||
|
(* Load the file into memory. *)
|
||||||
|
let index = read_whole_file tmpfile in
|
||||||
|
if delete_tmpfile then
|
||||||
|
(try Unix.unlink tmpfile with _ -> ());
|
||||||
|
|
||||||
|
(* Split file into lines. *)
|
||||||
|
let index = string_nsplit "\n" index in
|
||||||
|
|
||||||
|
(* If there is a signature (checked above) then remove it. *)
|
||||||
|
let index =
|
||||||
|
match index with
|
||||||
|
| "-----BEGIN PGP SIGNED MESSAGE-----" :: lines ->
|
||||||
|
(* Ignore all lines until we get to first blank. *)
|
||||||
|
let lines = dropwhile ((<>) "") lines in
|
||||||
|
(* Ignore the blank line too. *)
|
||||||
|
let lines = List.tl lines in
|
||||||
|
(* Take lines until we get to the end signature. *)
|
||||||
|
let lines = takewhile ((<>) "-----BEGIN PGP SIGNATURE-----") lines in
|
||||||
|
lines
|
||||||
|
| _ -> index in
|
||||||
|
|
||||||
|
(* Split into sections around each /^[/ *)
|
||||||
|
let rec loop = function
|
||||||
|
| [] -> []
|
||||||
|
| x :: xs when String.length x >= 1 && x.[0] = '[' ->
|
||||||
|
let lines = takewhile ((<>) "") xs in
|
||||||
|
let rest = dropwhile ((<>) "") xs in
|
||||||
|
if rest = [] then
|
||||||
|
[x, lines]
|
||||||
|
else (
|
||||||
|
let rest = List.tl rest in
|
||||||
|
let rest = loop rest in
|
||||||
|
(x, lines) :: rest
|
||||||
|
)
|
||||||
|
| x :: _ -> corrupt_line x
|
||||||
|
in
|
||||||
|
let sections = loop index in
|
||||||
|
|
||||||
|
(* Parse the fields in each section. *)
|
||||||
|
let isspace = function ' ' | '\t' -> true | _ -> false in
|
||||||
|
let starts_space str = String.length str >= 1 && isspace str.[0] in
|
||||||
|
let rec loop = function
|
||||||
|
| [] -> []
|
||||||
|
| x :: xs when not (starts_space x) && String.contains x '=' ->
|
||||||
|
let xs' = takewhile starts_space xs in
|
||||||
|
let ys = dropwhile starts_space xs in
|
||||||
|
(x :: xs') :: loop ys
|
||||||
|
| x :: _ -> corrupt_line x
|
||||||
|
in
|
||||||
|
let sections = List.map (fun (n, lines) -> n, loop lines) sections in
|
||||||
|
|
||||||
|
if debug then (
|
||||||
|
eprintf "index file (%s) after splitting:\n" source;
|
||||||
|
List.iter (
|
||||||
|
fun (n, fields) ->
|
||||||
|
eprintf " os-version: %s\n" n;
|
||||||
|
let i = ref 0 in
|
||||||
|
List.iter (
|
||||||
|
fun field ->
|
||||||
|
eprintf " %d: " !i;
|
||||||
|
List.iter prerr_endline field;
|
||||||
|
incr i
|
||||||
|
) fields
|
||||||
|
) sections
|
||||||
|
);
|
||||||
|
|
||||||
|
(* Now we've parsed the file into the correct sections, we
|
||||||
|
* interpret the meaning of the fields.
|
||||||
|
*)
|
||||||
|
let sections = List.map (
|
||||||
|
fun (n, fields) ->
|
||||||
|
let len = String.length n in
|
||||||
|
if len < 3 || n.[0] <> '[' || n.[len-1] <> ']' then
|
||||||
|
corrupt_line n;
|
||||||
|
let n = String.sub n 1 (len-2) in
|
||||||
|
|
||||||
|
let fields = List.map (
|
||||||
|
function
|
||||||
|
| [] -> assert false (* can never happen, I think? *)
|
||||||
|
| x :: xs when Str.string_match fieldname_rex x 0 ->
|
||||||
|
let field = Str.matched_group 1 x in
|
||||||
|
let rest_of_line = Str.matched_group 2 x in
|
||||||
|
let allow_multiline =
|
||||||
|
match field with
|
||||||
|
| "name" -> false
|
||||||
|
| "osinfo" -> false
|
||||||
|
| "file" -> false
|
||||||
|
| "sig" -> false
|
||||||
|
| "revision" -> false
|
||||||
|
| "format" -> false
|
||||||
|
| "size" -> false
|
||||||
|
| "compressed_size" -> false
|
||||||
|
| "expand" -> false
|
||||||
|
| "lvexpand" -> false
|
||||||
|
| "notes" -> true
|
||||||
|
| "hidden" -> false
|
||||||
|
| _ ->
|
||||||
|
eprintf "warning: unknown field '%s' in index (ignored)\n%!"
|
||||||
|
field;
|
||||||
|
true in
|
||||||
|
let value =
|
||||||
|
if not allow_multiline then (
|
||||||
|
if xs <> [] then (
|
||||||
|
eprintf (f_"virt-builder: field '%s' cannot span multiple lines\n")
|
||||||
|
field;
|
||||||
|
corrupt_line (List.hd xs)
|
||||||
|
);
|
||||||
|
rest_of_line
|
||||||
|
) else (
|
||||||
|
String.concat "\n" (rest_of_line :: xs)
|
||||||
|
) in
|
||||||
|
field, value
|
||||||
|
| x :: _ ->
|
||||||
|
corrupt_line x
|
||||||
|
) fields in
|
||||||
|
|
||||||
|
(n, fields)
|
||||||
|
) sections in
|
||||||
|
|
||||||
|
(* Check for repeated os-version names. *)
|
||||||
|
let nseen = Hashtbl.create 13 in
|
||||||
|
List.iter (
|
||||||
|
fun (n, _) ->
|
||||||
|
if Hashtbl.mem nseen n then (
|
||||||
|
eprintf (f_"virt-builder: index is corrupt: os-version '%s' appears two or more times\n") n;
|
||||||
|
corrupt_file ()
|
||||||
|
);
|
||||||
|
Hashtbl.add nseen n true
|
||||||
|
) sections;
|
||||||
|
|
||||||
|
(* Check for repeated fields. *)
|
||||||
|
List.iter (
|
||||||
|
fun (n, fields) ->
|
||||||
|
let fseen = Hashtbl.create 13 in
|
||||||
|
List.iter (
|
||||||
|
fun (field, _) ->
|
||||||
|
if Hashtbl.mem fseen field then (
|
||||||
|
eprintf (f_"virt-builder: index is corrupt: %s: field '%s' appears two or more times\n") n field;
|
||||||
|
corrupt_file ()
|
||||||
|
);
|
||||||
|
Hashtbl.add fseen field true
|
||||||
|
) fields
|
||||||
|
) sections;
|
||||||
|
|
||||||
|
(* Turn the sections into the final index. *)
|
||||||
|
let entries =
|
||||||
|
List.map (
|
||||||
|
fun (n, fields) ->
|
||||||
|
let printable_name =
|
||||||
|
try Some (List.assoc "name" fields) with Not_found -> None in
|
||||||
|
let osinfo =
|
||||||
|
try Some (List.assoc "osinfo" fields) with Not_found -> None in
|
||||||
|
let file_uri =
|
||||||
|
try make_absolute_uri (List.assoc "file" fields)
|
||||||
|
with Not_found ->
|
||||||
|
eprintf (f_"virt-builder: no 'file' (URI) entry for '%s'\n") n;
|
||||||
|
corrupt_file () in
|
||||||
|
let signature_uri =
|
||||||
|
try Some (make_absolute_uri (List.assoc "sig" fields))
|
||||||
|
with Not_found -> None in
|
||||||
|
let revision =
|
||||||
|
try int_of_string (List.assoc "revision" fields)
|
||||||
|
with
|
||||||
|
| Not_found -> 1
|
||||||
|
| Failure "int_of_string" ->
|
||||||
|
eprintf (f_"virt-builder: cannot parse 'revision' field for '%s'\n")
|
||||||
|
n;
|
||||||
|
corrupt_file () in
|
||||||
|
let format =
|
||||||
|
try Some (List.assoc "format" fields) with Not_found -> None in
|
||||||
|
let size =
|
||||||
|
try Int64.of_string (List.assoc "size" fields)
|
||||||
|
with
|
||||||
|
| Not_found ->
|
||||||
|
eprintf (f_"virt-builder: no 'size' field for '%s'\n") n;
|
||||||
|
corrupt_file ()
|
||||||
|
| Failure "int_of_string" ->
|
||||||
|
eprintf (f_"virt-builder: cannot parse 'size' field for '%s'\n")
|
||||||
|
n;
|
||||||
|
corrupt_file () in
|
||||||
|
let compressed_size =
|
||||||
|
try Some (Int64.of_string (List.assoc "compressed_size" fields))
|
||||||
|
with
|
||||||
|
| Not_found ->
|
||||||
|
None
|
||||||
|
| Failure "int_of_string" ->
|
||||||
|
eprintf (f_"virt-builder: cannot parse 'compressed_size' field for '%s'\n")
|
||||||
|
n;
|
||||||
|
corrupt_file () in
|
||||||
|
let expand =
|
||||||
|
try Some (List.assoc "expand" fields) with Not_found -> None in
|
||||||
|
let lvexpand =
|
||||||
|
try Some (List.assoc "lvexpand" fields) with Not_found -> None in
|
||||||
|
let notes =
|
||||||
|
try Some (List.assoc "notes" fields) with Not_found -> None in
|
||||||
|
let hidden =
|
||||||
|
try bool_of_string (List.assoc "hidden" fields)
|
||||||
|
with
|
||||||
|
| Not_found -> false
|
||||||
|
| Failure "bool_of_string" ->
|
||||||
|
eprintf (f_"virt-builder: cannot parse 'hidden' field for '%s'\n")
|
||||||
|
n;
|
||||||
|
corrupt_file () in
|
||||||
|
|
||||||
|
let entry = { printable_name = printable_name;
|
||||||
|
osinfo = osinfo;
|
||||||
|
file_uri = file_uri;
|
||||||
|
signature_uri = signature_uri;
|
||||||
|
revision = revision;
|
||||||
|
format = format;
|
||||||
|
size = size;
|
||||||
|
compressed_size = compressed_size;
|
||||||
|
expand = expand;
|
||||||
|
lvexpand = lvexpand;
|
||||||
|
notes = notes;
|
||||||
|
hidden = hidden } in
|
||||||
|
n, entry
|
||||||
|
) sections in
|
||||||
|
|
||||||
|
if debug then (
|
||||||
|
eprintf "index file (%s) after parsing:\n" source;
|
||||||
|
List.iter (print_entry Pervasives.stderr) entries
|
||||||
|
);
|
||||||
|
|
||||||
|
entries
|
||||||
|
|
||||||
|
(* Verify same-origin policy for the file= and sig= fields. *)
|
||||||
|
and make_absolute_uri path =
|
||||||
|
if String.length path = 0 then (
|
||||||
|
eprintf (f_"virt-builder: zero length path in the index file\n");
|
||||||
|
corrupt_file ()
|
||||||
|
)
|
||||||
|
else if string_find path "://" >= 0 then (
|
||||||
|
eprintf (f_"virt-builder: cannot use a URI ('%s') in the index file\n")
|
||||||
|
path;
|
||||||
|
corrupt_file ()
|
||||||
|
)
|
||||||
|
else if path.[0] = '/' then (
|
||||||
|
eprintf (f_"virt-builder: you must use relative paths (not '%s') in the index file\n") path;
|
||||||
|
corrupt_file ()
|
||||||
|
)
|
||||||
|
else (
|
||||||
|
(* Construct the URI. *)
|
||||||
|
try
|
||||||
|
let i = String.rindex source '/' in
|
||||||
|
String.sub source 0 (i+1) ^ path
|
||||||
|
with
|
||||||
|
Not_found -> source // path
|
||||||
|
)
|
||||||
|
in
|
||||||
|
|
||||||
|
get_index ()
|
||||||
35
builder/index_parser.mli
Normal file
35
builder/index_parser.mli
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
type index = (string * entry) list (* string = "os-version" *)
|
||||||
|
and entry = {
|
||||||
|
printable_name : string option; (* the name= field *)
|
||||||
|
osinfo : string option;
|
||||||
|
file_uri : string;
|
||||||
|
signature_uri : string option;
|
||||||
|
revision : int;
|
||||||
|
format : string option;
|
||||||
|
size : int64;
|
||||||
|
compressed_size : int64 option;
|
||||||
|
expand : string option;
|
||||||
|
lvexpand : string option;
|
||||||
|
notes : string option;
|
||||||
|
hidden : bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
val get_index : debug:bool -> downloader:Downloader.t -> sigchecker:Sigchecker.t -> string -> index
|
||||||
66
builder/list_entries.ml
Normal file
66
builder/list_entries.ml
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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
|
||||||
|
|
||||||
|
let list_entries ?(list_long = false) ~source index =
|
||||||
|
if list_long then (
|
||||||
|
printf (f_"Source URI: %s\n") source;
|
||||||
|
printf "\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
List.iter (
|
||||||
|
fun (name, { Index_parser.printable_name = printable_name;
|
||||||
|
size = size;
|
||||||
|
compressed_size = compressed_size;
|
||||||
|
notes = notes;
|
||||||
|
hidden = hidden }) ->
|
||||||
|
if not hidden then (
|
||||||
|
if not list_long then ( (* Short *)
|
||||||
|
printf "%-24s" name;
|
||||||
|
(match printable_name with
|
||||||
|
| None -> ()
|
||||||
|
| Some s -> printf " %s" s
|
||||||
|
);
|
||||||
|
printf "\n"
|
||||||
|
)
|
||||||
|
else ( (* Long *)
|
||||||
|
printf "%-24s %s\n" "os-version:" name;
|
||||||
|
(match printable_name with
|
||||||
|
| None -> ()
|
||||||
|
| Some name -> printf "%-24s %s\n" (s_"Full name:") name;
|
||||||
|
);
|
||||||
|
printf "%-24s %s\n" (s_"Minimum/default size:") (human_size size);
|
||||||
|
(match compressed_size with
|
||||||
|
| None -> ()
|
||||||
|
| Some size ->
|
||||||
|
printf "%-24s %s\n" (s_"Download size:") (human_size size);
|
||||||
|
);
|
||||||
|
(match notes with
|
||||||
|
| None -> ()
|
||||||
|
| Some notes ->
|
||||||
|
printf "\n";
|
||||||
|
printf "Notes:\n %s\n" notes
|
||||||
|
);
|
||||||
|
printf "\n"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) index
|
||||||
19
builder/list_entries.mli
Normal file
19
builder/list_entries.mli
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
val list_entries : ?list_long:bool -> source:string -> Index_parser.index -> unit
|
||||||
209
builder/sigchecker.ml
Normal file
209
builder/sigchecker.ml
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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
|
||||||
|
open Unix
|
||||||
|
|
||||||
|
let quote = Filename.quote
|
||||||
|
|
||||||
|
(* These are the public key and fingerprint belonging to
|
||||||
|
* Richard W.M. Jones who signs the templates on
|
||||||
|
* http://libguestfs.org/download/builder.
|
||||||
|
*)
|
||||||
|
let default_fingerprint = "F777 4FB1 AD07 4A7E 8C87 67EA 9173 8F73 E1B7 68A0"
|
||||||
|
let default_pubkey = "\
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Version: GnuPG v1.4.14 (GNU/Linux)
|
||||||
|
|
||||||
|
mQINBE6UMMEBEADM811hfTulaF4JpkVpAI10FImyb4ArvOiu8NdcUwTFo+cyWno3
|
||||||
|
U85B86H1Bsk/LgLTYtthSrTgsCtdxy+i5OaMjxZDIwKQ2+IYI3FCn9T3Mn28Idyh
|
||||||
|
kLHzrO9ph0Dv0BNfrlDZhQEC53aAFe/QxN7+A49BNBV7D1VAOOCsHjxMEDzcZkCa
|
||||||
|
oCrtXw1aNm2vkkj5ukbfukHAyLcQL7kow0qKPSVa1G4lfQP0WiG259Ydy+sUmbVb
|
||||||
|
TGdb6MEC84PQRDuw6/ZeoV04tn7ZNtQEMOS0uiciHOGfr2hBxQf9VIPNrHg42yaL
|
||||||
|
dOv51D99GuaxZ9E0HSoH/RwB1oXgd6rFdqVNYaBIQnnkwJANUEeGBArtIOZNCADT
|
||||||
|
Bt8vkSDm+lLEAFS+V8CACyW/LMIrGCvLdHeqtoAv0GDVyR2GPxldYfdtEmCUMWcb
|
||||||
|
Jlf71V9iAse2gUdoiHp5FfpGMkA5j7idKuxIws11XxRZJXXbBqiBqmVEAQ/v0m6p
|
||||||
|
kdo0MYTHydmecLuUK2bAGhpysfX97EfTSrxfrYphYWjTfKRD9GrADeZNfuz1DbKs
|
||||||
|
7LSqVaQJSjQrfgAwcnZLRaU0V4P5zxiz50gz1Aj3AZRL+Y3meZenzZTXcLFdnusg
|
||||||
|
wUfhhCuL3tluMtEh6tznumyxb43WO1yLwj6J6LtveiuJN1Z+KSQ6OieZcwARAQAB
|
||||||
|
tCVSaWNoYXJkIFcuTS4gSm9uZXMgPHJpY2hAYW5uZXhpYS5vcmc+iQI4BBMBAgAi
|
||||||
|
BQJOlDDBAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCRc49z4bdooHQY
|
||||||
|
D/wJLklSZNyXIW+rG5sUbg7j9cTIF5p/lB9kI2yx6KodJp/2knKyvnmzz0gBw/OE
|
||||||
|
HL4E4UW26oWKo+36I8wkBnuGa6UtANeITcJqFE19VpHEXHsxre64jNQnO8/w748W
|
||||||
|
1ROW+Ry43xmrlRWKuCm4oPYUzlp0fq9ATAne8eblfG+NOs8DYuA8xZNQzFaI2kDC
|
||||||
|
QLD4YoXLoNsP27Koga36b0KwxPFD9tyVZiu9XDH/3hMN7Nb15B66PFr+HcMmQ67G
|
||||||
|
nUIN5ulcIwj38i40cyaTs1VRheOzTHXE/a6Q2AhMKiKqOoEjQ73/mV7cAVoPtM3o
|
||||||
|
83Q/8aVKBH0bVRwAeV1tju6b14fqKoG0zNBEcXdlSkht6ScxJYIc/LPUxAMDwgSE
|
||||||
|
OWshjmeRzKXypBbHn/DP8QVyM2gk5wY+mMSH7MpR0p/hgj+rFO8H9L7pC4dCog3E
|
||||||
|
qzrYhRN+TaP6MPH3WkOwPH4d4IfQRFnHp+VPYPijKEiLrUl/o8k3DyAanAPBpJ/x
|
||||||
|
na4wXAjlFBctOq6g+SrCUiHpwk7b2YNwGgr5Vl3GmZELzK/G8gg3uJYKQ9Bpv16t
|
||||||
|
WWOz+IFiOFa0UULeo0QPmFAIMZiDojNsY1SwBKB3ZL1YWZezgMdQAbpze/IXoSt7
|
||||||
|
zxWJoKH2jK7q9mvFiaY12l2YnKuCcegWVAViLxRpBnrbz7QmUmljaGFyZCBXLk0u
|
||||||
|
IEpvbmVzIDxyam9uZXNAcmVkaGF0LmNvbT6JAjgEEwECACIFAk6UOQsCGwMGCwkI
|
||||||
|
BwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEJFzj3Pht2igIUYQAKomI0edLakahsUQ
|
||||||
|
MxOZuhBbXJ4/VWF8bXYChDNPKvJp5nB7fBXujJ+39cIUM5fe2ViO6qSDpFC29imx
|
||||||
|
F5pPbAqspZBPBkLLiZLji8R42hGarntdtTW0UWSBpq+nC5+G1psrnATI3uXGNxKQ
|
||||||
|
R99c5HoMY7dBC2Y8TCGE64NINZ/XVh472s6IGLPn8MTn26YdRKC9BrVkCFMP2OBr
|
||||||
|
6D4IprnyTAWAzb68ew20QmyWO+NBi9MplaDNQVl8PIOgfpyWlkgX1z9m67pcSDkw
|
||||||
|
46hksp0yuOD1VwR4iVZ2/CmIsGRUlx41vWD6BIp9KxKyDIU1CYTRhq72dahHsl/8
|
||||||
|
BjCndV5PO0GphqfCzmCv4DXjUwmrMTbH/GFnt5rfwcMcXUgcK0vV9vQ2SOU56Zd1
|
||||||
|
fb27ZCFJKZc0Fu8krwFldCp/NYILf6ogUL/C1hfuCGSSuyDVY16Gg3dla1x+6zpF
|
||||||
|
asnWQlaw8xT5LlMWvTZs5WsoSVHu7dVZWlgxINP++hlZrTz/S8l38yyQ15YFFl3W
|
||||||
|
9M7dzkegOeDTPfx6B89WgfvfJjA/D0/FYxxWPXEtrn9DlJ4daEJqNsrvfLErz9R8
|
||||||
|
4IQmfmhR93j+rdotner+6keC/wVByEfbW1wmXtmFKXQ6srdpj8VKRFrvkyXVgepM
|
||||||
|
DypLgRH2v7lL2kdWhUu2y4EAgrwzuQINBE6UMMEBEADxQxMgUuDrw5GT4tqARTPI
|
||||||
|
SSdNcUsRxRhVA8srYOyECliE+B3TwcRDFBs+MyPFJVEuX8fi4eGj/AK5t1GHerfk
|
||||||
|
orUGlz72q4c7LLhkfZrsuJbk2dgkjvldKJnIazQJa6epGLqdsE5RlmSgwedIbtMd
|
||||||
|
naGJBQH8aKP/Wi1+wUxsm5N3p7+R2WRx48VfpEhYB+Zf/FkFm1Ycjwh57KQ0+OHw
|
||||||
|
ykf8VfMisxuH30tDxOCV+VptWKfOF2rDNdaNPWhij2YIjhJXRpkuRR+1PpI4jLaD
|
||||||
|
JxcVZmG/0zucacupUN2g5OUH59ySU/totD6YMnmp3FONoyF1uIEJo6Vs30npHGkO
|
||||||
|
XgBo3Pxt7oLJeykLPtdSLgm3cwXIYMWarVsAkKNXitQIVGpVRLeaK373VwmXFqoi
|
||||||
|
M2SMHeawTUdOORFjpQzkknlJWM1TmUVtHHKt8Pl9+/5+wXKyt2IDdcUkMrB6K5qF
|
||||||
|
fb7EwVhoI8ehJQK+eeDCjFwCAiwB3iV8JlyW+tEU7JuyXOQlwY1VWm/WqMD8gaRi
|
||||||
|
rT+RFDFliZ3tQbW2pqUoZBROV5HN4tieDfwxGKCvk6Tsdb30zA9DPQp93+238bYf
|
||||||
|
312sg9R+CD0AqxoxFG5FJu4HShcPRrPnYtRZqKRe40GDWvBEArXZprwL1qrP+Kl/
|
||||||
|
mRrEQpxAGIoFG8HbVvD3EQARAQABiQIfBBgBAgAJBQJOlDDBAhsMAAoJEJFzj3Ph
|
||||||
|
t2igSLQP/2uIrAY2CDr0kWBJiD3TztiHy8IdxwUpyTBTebwmAbi44/EvtJfIisrG
|
||||||
|
YjKIEv/w0E61gO7O1JBG4+IG93W+v9fTT/e39JMyxsYqoZZHUhP11Okx5grDS5b0
|
||||||
|
O8VXOmXVRMdVNfstRBr10HD9uNDq7ruKD18TxYTwN0GPD4gj1dbHQDR77Tr5cyBs
|
||||||
|
6Ou5PBOH4r3qcqf/cJUSMeUUu75xLwixux6E7tD2S+t6F07wlWxntUcPtzyAHj20
|
||||||
|
J89orUC+dT6r6MypBoI0jdJCp9JPGtR7i+fE5Gm4E5+AUSubLPtZGRY9Um2eMoS2
|
||||||
|
DnQpGOKx1VvsixR/Kw44j2tRAvmYMS4iDKcuZU+nZ+xokAgObILj/b9n/Qe2/fXy
|
||||||
|
CFdcgSvbm+dV1fZxsdMF/P9OU8aqdT9A9Fv5y+cDMEg4DVnhwMJTxGh/TCkw/H+A
|
||||||
|
frHEtRc98lSQN5odpITNG17mG6JOdHM+wA57qHH0uy4+5RsbyAJahcdBcmObK/RF
|
||||||
|
i4WZlThpbHftX5O/LH98aYQ2fJayIxv1EAjzOBOQ0MfBHI0KCJR1pysEisX28sJA
|
||||||
|
Ic73gnJJ3BLZbqfBRgxjNMNroxC+5Tw6uPGFHa3YnuIAxxw0HcDVZ9vnTWBWFPGw
|
||||||
|
ZvXkQ3FVJwZoLmHw47vvlVpLD/4gi1SuHWieRvZ+UdDq00E348pm
|
||||||
|
=neBW
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
"
|
||||||
|
let key_imported = ref false
|
||||||
|
|
||||||
|
type t = {
|
||||||
|
debug : bool;
|
||||||
|
gpg : string;
|
||||||
|
fingerprint : string;
|
||||||
|
check_signature : bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
let create ~debug ~gpg ?(fingerprint = default_fingerprint) ~check_signature =
|
||||||
|
{
|
||||||
|
debug = debug;
|
||||||
|
gpg = gpg;
|
||||||
|
fingerprint = fingerprint;
|
||||||
|
check_signature = check_signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
(* Compare two strings of hex digits ignoring whitespace and case. *)
|
||||||
|
let rec equal_fingerprints fp1 fp2 =
|
||||||
|
let len1 = String.length fp1 and len2 = String.length fp2 in
|
||||||
|
let rec loop i j =
|
||||||
|
if i = len1 && j = len2 then true (* match! *)
|
||||||
|
else if i = len1 || j = len2 then false (* no match - different lengths *)
|
||||||
|
else (
|
||||||
|
let x1 = getxdigit fp1.[i] and x2 = getxdigit fp2.[j] in
|
||||||
|
match x1, x2 with
|
||||||
|
| Some x1, Some x2 when x1 = x2 -> loop (i+1) (j+1)
|
||||||
|
| Some x1, Some x2 -> false (* no match - different content *)
|
||||||
|
| Some _, None -> loop i (j+1)
|
||||||
|
| None, Some _ -> loop (i+1) j
|
||||||
|
| None, None -> loop (i+1) (j+1)
|
||||||
|
)
|
||||||
|
in
|
||||||
|
loop 0 0
|
||||||
|
|
||||||
|
and getxdigit = function
|
||||||
|
| '0'..'9' as c -> Some (Char.code c - Char.code '0')
|
||||||
|
| 'a'..'f' as c -> Some (Char.code c - Char.code 'a')
|
||||||
|
| 'A'..'F' as c -> Some (Char.code c - Char.code 'A')
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
|
let rec verify t filename =
|
||||||
|
if t.check_signature then (
|
||||||
|
let args = quote filename in
|
||||||
|
do_verify t args
|
||||||
|
)
|
||||||
|
|
||||||
|
and verify_detached t filename sigfile =
|
||||||
|
if t.check_signature then (
|
||||||
|
match sigfile with
|
||||||
|
| None ->
|
||||||
|
eprintf (f_"virt-builder: error: there is no detached signature file\nThis probably means the index file is missing a sig=... line.\nYou can use --no-check-signature to ignore this error, but that means\nyou are susceptible to man-in-the-middle attacks.\n");
|
||||||
|
exit 1
|
||||||
|
| Some sigfile ->
|
||||||
|
let args = sprintf "%s %s" (quote sigfile) (quote filename) in
|
||||||
|
do_verify t args
|
||||||
|
)
|
||||||
|
|
||||||
|
and do_verify t args =
|
||||||
|
import_key t;
|
||||||
|
|
||||||
|
let status_file = Filename.temp_file "vbstat" ".txt" in
|
||||||
|
let cmd =
|
||||||
|
sprintf "%s --verify%s --status-file %s %s"
|
||||||
|
t.gpg (if t.debug then "" else " -q --logger-file /dev/null")
|
||||||
|
(quote status_file) args in
|
||||||
|
if t.debug then eprintf "%s\n%!" cmd;
|
||||||
|
let r = Sys.command cmd in
|
||||||
|
if r <> 0 then (
|
||||||
|
eprintf (f_"virt-builder: error: GPG failure: could not verify digital signature of file\nTry:\n - Use the '-v' option and look for earlier error messages.\n - Delete the cache: virt-builder --delete-cache\n - Check no one has tampered with the website or your network!\n");
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
|
||||||
|
(* Check the fingerprint is who it should be. *)
|
||||||
|
let status = read_whole_file status_file in
|
||||||
|
unlink status_file;
|
||||||
|
|
||||||
|
let status = string_nsplit "\n" status in
|
||||||
|
let fingerprint = ref "" in
|
||||||
|
List.iter (
|
||||||
|
fun line ->
|
||||||
|
let line = string_nsplit " " line in
|
||||||
|
match line with
|
||||||
|
| "[GNUPG:]" :: "VALIDSIG" :: fp :: _ -> fingerprint := fp
|
||||||
|
| _ -> ()
|
||||||
|
) status;
|
||||||
|
|
||||||
|
if not (equal_fingerprints !fingerprint t.fingerprint) then (
|
||||||
|
eprintf (f_"virt-builder: error: fingerprint of signature does not match the expected fingerprint!\n found fingerprint: %s\n expected fingerprint: %s\n")
|
||||||
|
!fingerprint t.fingerprint;
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
|
||||||
|
(* Import the default public key, if it's the default fingerprint. *)
|
||||||
|
and import_key t =
|
||||||
|
if not !key_imported && equal_fingerprints t.fingerprint default_fingerprint
|
||||||
|
then (
|
||||||
|
let filename, chan = Filename.open_temp_file "vbpubkey" ".asc" in
|
||||||
|
output_string chan default_pubkey;
|
||||||
|
close_out chan;
|
||||||
|
|
||||||
|
let cmd = sprintf "%s --import %s%s"
|
||||||
|
t.gpg (quote filename)
|
||||||
|
(if t.debug then "" else " >/dev/null 2>&1") in
|
||||||
|
let r = Sys.command cmd in
|
||||||
|
if r <> 0 then (
|
||||||
|
eprintf (f_"virt-builder: error: could not import public key\nUse the '-v' option and look for earlier error messages.\n");
|
||||||
|
exit 1
|
||||||
|
);
|
||||||
|
unlink filename;
|
||||||
|
key_imported := true
|
||||||
|
)
|
||||||
28
builder/sigchecker.mli
Normal file
28
builder/sigchecker.mli
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
(* virt-builder
|
||||||
|
* Copyright (C) 2013 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.
|
||||||
|
*)
|
||||||
|
|
||||||
|
type t
|
||||||
|
|
||||||
|
val create : debug:bool -> gpg:string -> ?fingerprint:string -> check_signature:bool -> t
|
||||||
|
|
||||||
|
val verify : t -> string -> unit
|
||||||
|
(** Verify the file is signed (if check_signature is true). *)
|
||||||
|
|
||||||
|
val verify_detached : t -> string -> string option -> unit
|
||||||
|
(** Verify the file is signed against the detached signature
|
||||||
|
(if check_signature is true). *)
|
||||||
20
builder/test-virt-builder.sh
Executable file
20
builder/test-virt-builder.sh
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash -
|
||||||
|
# libguestfs virt-builder test script
|
||||||
|
# Copyright (C) 2013 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
|
||||||
986
builder/virt-builder.pod
Normal file
986
builder/virt-builder.pod
Normal file
@@ -0,0 +1,986 @@
|
|||||||
|
=encoding utf8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
virt-builder - Build virtual machine images quickly
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
virt-builder [-o|--output DISKIMAGE] [--size SIZE] [--format raw|qcow2]
|
||||||
|
[--attach ISOFILE]
|
||||||
|
[--install PKG,[PKG...]]
|
||||||
|
[--root-password ...]
|
||||||
|
[--upload FILE:DEST]
|
||||||
|
[--run SCRIPT] [--run-command 'CMD ARGS ...']
|
||||||
|
[--firstboot SCRIPT] [--firstboot-command 'CMD ARGS ...']
|
||||||
|
[--firstboot-install PKG,[PKG...]]
|
||||||
|
os-version
|
||||||
|
|
||||||
|
virt-builder -l|--list [--long]
|
||||||
|
|
||||||
|
virt-builder --delete-cache
|
||||||
|
|
||||||
|
virt-builder --get-kernel DISKIMAGE
|
||||||
|
[--format raw|qcow2] [--output OUTPUTDIR]
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
Virt-builder is a tool for quickly building new virtual machines. You
|
||||||
|
can build a variety of VMs for local or cloud use, usually within a
|
||||||
|
few minutes or less. Virt-builder also has many ways to customize
|
||||||
|
these VMs. Everything is run from the command line and nothing
|
||||||
|
requires root privileges, so automation and scripting is simple.
|
||||||
|
|
||||||
|
Note that virt-builder does not install guests from scratch. It takes
|
||||||
|
cleanly prepared, digitally signed OSes and customizes them. This
|
||||||
|
approach is used because it is much faster, but if you need to do
|
||||||
|
fresh installs you may want to look at L<virt-install(1)> and
|
||||||
|
L<oz-install(1)>.
|
||||||
|
|
||||||
|
The easiest way to get started is by looking at the examples in the
|
||||||
|
next section.
|
||||||
|
|
||||||
|
=head1 EXAMPLES
|
||||||
|
|
||||||
|
=head2 List the virtual machines available
|
||||||
|
|
||||||
|
virt-builder --list
|
||||||
|
|
||||||
|
will list out the operating systems available to install. A selection
|
||||||
|
of freely redistributable OSes is available as standard. You can add
|
||||||
|
your own too (see below).
|
||||||
|
|
||||||
|
=head2 Build a virtual machine
|
||||||
|
|
||||||
|
virt-builder fedora-20
|
||||||
|
|
||||||
|
will build a Fedora 20 image. This will have all default
|
||||||
|
configuration (minimal size, no user accounts, random root password,
|
||||||
|
only the bare minimum installed software, etc.).
|
||||||
|
|
||||||
|
Note you I<do not need to run this command as root>.
|
||||||
|
|
||||||
|
The first time this runs it has to download the template over the
|
||||||
|
network, but this gets cached (see L</CACHING>).
|
||||||
|
|
||||||
|
The name of the output file is derived from the template name, so
|
||||||
|
above it will be C<fedora-20.img>. You can change the output filename
|
||||||
|
using the I<-o> option:
|
||||||
|
|
||||||
|
virt-builder fedora-20 -o mydisk.img
|
||||||
|
|
||||||
|
You can also use the I<-o> option to write to existing devices or
|
||||||
|
logical volumes.
|
||||||
|
|
||||||
|
virt-builder fedora-20 --format qcow2
|
||||||
|
|
||||||
|
As above, but write the output in qcow2 format to C<fedora-20.qcow2>.
|
||||||
|
|
||||||
|
virt-builder fedora-20 --size 20G
|
||||||
|
|
||||||
|
As above, but the output size will be 20 GB. The guest OS is resized
|
||||||
|
as it is copied to the output (automatically, using
|
||||||
|
L<virt-resize(1)>).
|
||||||
|
|
||||||
|
=head2 Setting the root password
|
||||||
|
|
||||||
|
virt-builder fedora-20 --root-password file:/tmp/rootpw
|
||||||
|
|
||||||
|
Create a Fedora 20 image. The root password is taken from the file
|
||||||
|
C</tmp/rootpw>.
|
||||||
|
|
||||||
|
Note if you I<don't> set I<--root-password> then the guest is given
|
||||||
|
a I<random> root password.
|
||||||
|
|
||||||
|
You can also create user accounts. See L</USERS AND PASSWORDS> below.
|
||||||
|
|
||||||
|
=head2 Installing software
|
||||||
|
|
||||||
|
To install packages from the ordinary (guest) software repository
|
||||||
|
(eg. yum or apt):
|
||||||
|
|
||||||
|
virt-builder fedora-20 --install "inkscape,+Xfce Desktop"
|
||||||
|
|
||||||
|
C<+> is used to install groups of packages (see L<yum(8)>).
|
||||||
|
|
||||||
|
=head2 Customizing the installation
|
||||||
|
|
||||||
|
There are four options that let you run shell scripts to customize the
|
||||||
|
installation. They are: I<--run>/I<--run-command>, which run a shell
|
||||||
|
script or command while the disk image is being generated and lets you
|
||||||
|
add or edit files that go into the disk image. And
|
||||||
|
I<--firstboot>/I<--firstboot-command>, which let you add
|
||||||
|
scripts/commands that are run the first time the guest boots.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
cat <<'EOF' > /tmp/yum-update.sh
|
||||||
|
yum -y update
|
||||||
|
EOF
|
||||||
|
|
||||||
|
virt-builder fedora-20 --firstboot /tmp/yum-update.sh
|
||||||
|
|
||||||
|
or simply:
|
||||||
|
|
||||||
|
virt-builder fedora-20 --firstboot-command 'yum -y update'
|
||||||
|
|
||||||
|
which makes the C<yum update> command run once the first time the
|
||||||
|
guest boots.
|
||||||
|
|
||||||
|
Or:
|
||||||
|
|
||||||
|
cat <<'EOF' > /tmp/no-gpg-sigs.sh
|
||||||
|
sed -i 's/gpgcheck=1/gpgcheck=0/' /etc/yum.conf
|
||||||
|
EOF
|
||||||
|
|
||||||
|
virt-builder fedora-20 --run /tmp/no-gpg-sigs.sh
|
||||||
|
|
||||||
|
which edits C</etc/yum.conf> inside the disk image (during disk image
|
||||||
|
creation, long before boot).
|
||||||
|
|
||||||
|
You can combine these options, and have multiple of either or both
|
||||||
|
sets of scripts.
|
||||||
|
|
||||||
|
=head1 OPTIONS
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item B<--help>
|
||||||
|
|
||||||
|
Display help.
|
||||||
|
|
||||||
|
=item B<--attach> ISOFILE
|
||||||
|
|
||||||
|
During the customization phase, the given disk is attached to the
|
||||||
|
libguestfs appliance. This is used to provide extra software
|
||||||
|
repositories or other data for customization.
|
||||||
|
|
||||||
|
You probably want to ensure the volume(s) or filesystems in the
|
||||||
|
attached disks are labelled (or an ISO volume name) so that you can
|
||||||
|
mount them by label in your run-scripts:
|
||||||
|
|
||||||
|
mkdir /tmp/mount
|
||||||
|
mount LABEL=EXTRA /tmp/mount
|
||||||
|
|
||||||
|
You can have multiple I<--attach> options, and the format can be any
|
||||||
|
disk format (not just an ISO).
|
||||||
|
|
||||||
|
See also: I<--run>,
|
||||||
|
L</Installing packages at build time from a side repository>,
|
||||||
|
L<virt-make-fs(1)>.
|
||||||
|
|
||||||
|
=item B<--attach-format> FORMAT
|
||||||
|
|
||||||
|
Specify the disk format for the next I<--attach> option. The
|
||||||
|
C<FORMAT> is usually C<raw> or C<qcow2>. Use C<raw> for ISOs.
|
||||||
|
|
||||||
|
=item B<--cache> DIR
|
||||||
|
|
||||||
|
=item B<--no-cache>
|
||||||
|
|
||||||
|
I<--cache> DIR sets the directory to use/check for cached template
|
||||||
|
files. If not set, defaults to either
|
||||||
|
C<$XDG_CACHE_HOME/virt-builder/> or C<$HOME/.cache/virt-builder/>.
|
||||||
|
|
||||||
|
I<--no-cache> disables template caching.
|
||||||
|
|
||||||
|
=item B<--check-signature>
|
||||||
|
|
||||||
|
=item B<--no-check-signature>
|
||||||
|
|
||||||
|
Check/don't check the digital signature of the OS template. The
|
||||||
|
default is to check the signature and exit if it is not correct.
|
||||||
|
Using I<--no-check-signature> bypasses this check.
|
||||||
|
|
||||||
|
See also I<--fingerprint>.
|
||||||
|
|
||||||
|
=item B<--curl> CURL
|
||||||
|
|
||||||
|
Specify an alternate L<curl(1)> binary. You can also use this to add
|
||||||
|
curl parameters, for example to disable https certificate checks:
|
||||||
|
|
||||||
|
virt-builder --curl "curl --insecure" [...]
|
||||||
|
|
||||||
|
=item B<--delete-cache>
|
||||||
|
|
||||||
|
Delete the template cache. See L</CACHING>.
|
||||||
|
|
||||||
|
=item B<--fingerprint> 'AAAA BBBB ...'
|
||||||
|
|
||||||
|
Check that the digital signature is signed by the key with the given
|
||||||
|
fingerprint. (The fingerprint is a long string, usually written as 10
|
||||||
|
groups of 4 hexadecimal digits).
|
||||||
|
|
||||||
|
If signature checking is enabled and the I<--fingerprint> option is
|
||||||
|
not given, then this checks the download was signed by
|
||||||
|
S<F777 4FB1 AD07 4A7E 8C87 67EA 9173 8F73 E1B7 68A0> (which is
|
||||||
|
S<Richard W.M. Jones's> key).
|
||||||
|
|
||||||
|
You can also set the C<VIRT_BUILDER_FINGERPRINT> environment variable.
|
||||||
|
|
||||||
|
=item B<--firstboot> SCRIPT
|
||||||
|
|
||||||
|
=item B<--firstboot-command> 'CMD ARGS ...'
|
||||||
|
|
||||||
|
Install C<SCRIPT> inside the guest, so that when the guest first boots
|
||||||
|
up, the script runs (as root, late in the boot process).
|
||||||
|
|
||||||
|
The script is automatically chmod +x after installation in the guest.
|
||||||
|
|
||||||
|
The alternative version I<--firstboot-command> is the same, but it
|
||||||
|
conveniently wraps the command up in a single line script for you.
|
||||||
|
|
||||||
|
You can have multiple I<--firstboot> and I<--firstboot-command>
|
||||||
|
options. They run in the same order that they appear on the command
|
||||||
|
line.
|
||||||
|
|
||||||
|
See also I<--run>.
|
||||||
|
|
||||||
|
=item B<--firstboot-install> PKG[,PKG,...]
|
||||||
|
|
||||||
|
Install the named packages (a comma-separated list). These are
|
||||||
|
installed when the guest first boots using the guest's package manager
|
||||||
|
(eg. apt, yum, etc.) and the guest's network connection.
|
||||||
|
|
||||||
|
For an overview on the different ways to install packages, see
|
||||||
|
L</INSTALLING PACKAGES>.
|
||||||
|
|
||||||
|
=item B<--format> qcow2
|
||||||
|
|
||||||
|
=item B<--format> raw
|
||||||
|
|
||||||
|
Select the output format. The default is I<raw>.
|
||||||
|
|
||||||
|
=item B<--get-kernel> IMAGE
|
||||||
|
|
||||||
|
This option extracts the kernel and initramfs from a previously built
|
||||||
|
disk image called C<IMAGE> (in fact it works for any VM disk image,
|
||||||
|
not just ones built using virt-builder).
|
||||||
|
|
||||||
|
The kernel and initramfs are written to the current directory, unless
|
||||||
|
you also specify the I<--output> C<outputdir> B<directory> name.
|
||||||
|
|
||||||
|
The format of the disk image is automatically detected unless you
|
||||||
|
specify it by using the I<--format> option.
|
||||||
|
|
||||||
|
In the case where the guest contains multiple kernels, the one with
|
||||||
|
the highest version number is chosen. To extract arbitrary kernels
|
||||||
|
from the disk image, see L<guestfish(1)>. To extract the entire
|
||||||
|
C</boot> directory of a guest, see L<virt-copy-out(1)>.
|
||||||
|
|
||||||
|
=item B<--gpg> GPG
|
||||||
|
|
||||||
|
Specify an alternate L<gpg(1)> (GNU Privacy Guard) binary. You can
|
||||||
|
also use this to add gpg parameters, for example to specify an
|
||||||
|
alternate home directory:
|
||||||
|
|
||||||
|
virt-builder --gpg "gpg --homedir /tmp" [...]
|
||||||
|
|
||||||
|
=item B<--install> PKG[,PKG,...]
|
||||||
|
|
||||||
|
Install the named packages (a comma-separated list). These are
|
||||||
|
installed during the image build using the guest's package manager
|
||||||
|
(eg. apt, yum, etc.) and the host's network connection.
|
||||||
|
|
||||||
|
For an overview on the different ways to install packages, see
|
||||||
|
L</INSTALLING PACKAGES>.
|
||||||
|
|
||||||
|
=item B<-l>
|
||||||
|
|
||||||
|
=item B<--list>
|
||||||
|
|
||||||
|
=item B<--list --long>
|
||||||
|
|
||||||
|
List available templates.
|
||||||
|
|
||||||
|
The alternative I<--list --long> form shows lots more details about
|
||||||
|
each operating system option.
|
||||||
|
|
||||||
|
See also: I<--source>, L</CREATING YOUR OWN TEMPLATES>.
|
||||||
|
|
||||||
|
=item B<--network>
|
||||||
|
|
||||||
|
=item B<--no-network>
|
||||||
|
|
||||||
|
Enable or disable network access from the guest during the installation.
|
||||||
|
|
||||||
|
Enabled is the default. Use I<--no-network> to disable access.
|
||||||
|
|
||||||
|
If you use I<--no-network> then certain other options such as
|
||||||
|
I<--install> will not work.
|
||||||
|
|
||||||
|
This does not affect whether the guest can access the network once it
|
||||||
|
has been booted, because that is controlled by your hypervisor or
|
||||||
|
cloud environment and has nothing to do with virt-builder.
|
||||||
|
|
||||||
|
Generally speaking you should I<not> use I<--no-network>. But here
|
||||||
|
are some reasons why you might want to:
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item 1.
|
||||||
|
|
||||||
|
Because the libguestfs backend that you are using doesn't support the
|
||||||
|
network. (See: L<guestfs(3)/BACKEND>).
|
||||||
|
|
||||||
|
=item 2.
|
||||||
|
|
||||||
|
Any software you need to install comes from an attached ISO, so you
|
||||||
|
don't need the network.
|
||||||
|
|
||||||
|
=item 3.
|
||||||
|
|
||||||
|
You don't want untrusted guest code trying to access your host network
|
||||||
|
when running virt-builder. This is particularly an issue when you
|
||||||
|
don't trust the source of the operating system templates.
|
||||||
|
|
||||||
|
=item 4.
|
||||||
|
|
||||||
|
You don't have a host network (eg. in secure/restricted environments).
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item B<-o> filename
|
||||||
|
|
||||||
|
=item B<--output> filename
|
||||||
|
|
||||||
|
Write the output to C<filename>. If you don't specify this option,
|
||||||
|
then the output filename is generated by taking the C<os-version> or
|
||||||
|
basename of the template, removing any extensions, and adding C<.img>
|
||||||
|
(for raw format) or C<.qcow2> (for qcow2 format).
|
||||||
|
|
||||||
|
Note that the output filename could be a device, partition or logical
|
||||||
|
volume.
|
||||||
|
|
||||||
|
=item B<--password-crypto> password-crypto
|
||||||
|
|
||||||
|
Set the password encryption to C<md5>, C<sha256> or C<sha512>.
|
||||||
|
|
||||||
|
C<sha256> and C<sha512> require glibc E<ge> 2.7 (check crypt(3) inside
|
||||||
|
the guest).
|
||||||
|
|
||||||
|
C<md5> will work with relatively old Linux guests (eg. RHEL 3), but
|
||||||
|
is not secure against modern attacks.
|
||||||
|
|
||||||
|
The default is C<sha512> unless libguestfs detects an old guest that
|
||||||
|
didn't have support for SHA-512, in which case it will use C<md5>.
|
||||||
|
You can override libguestfs by specifying this option.
|
||||||
|
|
||||||
|
=item B<--quiet>
|
||||||
|
|
||||||
|
Don't print ordinary progress messages.
|
||||||
|
|
||||||
|
=item B<--root-password> PASSWORD
|
||||||
|
|
||||||
|
Set the root password.
|
||||||
|
|
||||||
|
See L</USERS AND PASSWORDS> below for the format of the C<PASSWORD>
|
||||||
|
field, and also how to set up user accounts.
|
||||||
|
|
||||||
|
Note if you I<don't> set I<--root-password> then the guest is given
|
||||||
|
a I<random> root password.
|
||||||
|
|
||||||
|
=item B<--run> SCRIPT
|
||||||
|
|
||||||
|
=item B<--run-command> 'CMD ARGS ...'
|
||||||
|
|
||||||
|
Run the shell script (or any program) called C<SCRIPT> on the disk
|
||||||
|
image. The script runs virtualized inside a small appliance, chrooted
|
||||||
|
into the guest filesystem.
|
||||||
|
|
||||||
|
The script is automatically chmod +x.
|
||||||
|
|
||||||
|
If libguestfs supports it then a limited network connection is
|
||||||
|
available but it only allows outgoing network connections. You can
|
||||||
|
also attach data disks (eg. ISO files) as another way to provide data
|
||||||
|
(eg. software packages) to the script without needing a network
|
||||||
|
connection.
|
||||||
|
|
||||||
|
The alternative version I<--run-command> is the same, but it
|
||||||
|
conveniently wraps the command up in a single line script for you.
|
||||||
|
|
||||||
|
You can have multiple I<--run> and I<--run-command> options. They run
|
||||||
|
in the same order that they appear on the command line.
|
||||||
|
|
||||||
|
See also I<--firstboot>, I<--attach>.
|
||||||
|
|
||||||
|
=item B<--size> SIZE
|
||||||
|
|
||||||
|
Select the size, where the size can be specified using common names
|
||||||
|
such as C<32G> (32 gigabytes) etc.
|
||||||
|
|
||||||
|
If the size is not specified, then one of two things happens. If the
|
||||||
|
output is a file, then the size is the same as the template (this is
|
||||||
|
most likely I<not> what you want). If the output is a device,
|
||||||
|
partition, etc then the size of that device is used.
|
||||||
|
|
||||||
|
=item B<--source> URL
|
||||||
|
|
||||||
|
Set the source URL to look for templates. If not specified it
|
||||||
|
defaults to L<http://libguestfs.org/download/builder/index.asc>
|
||||||
|
|
||||||
|
See also L</CREATING YOUR OWN TEMPLATES> below.
|
||||||
|
|
||||||
|
You can also set the C<VIRT_BUILDER_SOURCE> environment variable.
|
||||||
|
|
||||||
|
Note that you should not point I<--source> to sources that you don't
|
||||||
|
trust (unless the source is signed by someone you do trust). See also
|
||||||
|
the I<--no-network> option.
|
||||||
|
|
||||||
|
=item B<--upload> FILE:DEST
|
||||||
|
|
||||||
|
Upload local file C<FILE> to destination C<DEST> in the disk image.
|
||||||
|
File owner and permissions from the original are preserved, so you
|
||||||
|
should set them to what you want them to be in the disk image.
|
||||||
|
|
||||||
|
=item B<-v>
|
||||||
|
|
||||||
|
=item B<--verbose>
|
||||||
|
|
||||||
|
Enable debug messages and/or produce verbose output.
|
||||||
|
|
||||||
|
When reporting bugs, use this option and attach the complete output to
|
||||||
|
your bug report.
|
||||||
|
|
||||||
|
=item B<-V>
|
||||||
|
|
||||||
|
=item B<--version>
|
||||||
|
|
||||||
|
Display version number and exit.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 REFERENCE
|
||||||
|
|
||||||
|
=head2 INSTALLING PACKAGES
|
||||||
|
|
||||||
|
There are several approaches to installing packages or applications in
|
||||||
|
the guest which have different trade-offs.
|
||||||
|
|
||||||
|
=head3 Installing packages at build time
|
||||||
|
|
||||||
|
If the guest OS you are installing is similar to the host OS (eg.
|
||||||
|
both are Linux), and if libguestfs supports network connections, then
|
||||||
|
you can use I<--install> to install packages like this:
|
||||||
|
|
||||||
|
virt-builder fedora-20 --install inkscape
|
||||||
|
|
||||||
|
This uses the guest's package manager but the host's network
|
||||||
|
connection.
|
||||||
|
|
||||||
|
=head3 Installing packages at first boot
|
||||||
|
|
||||||
|
Another option is to install the packages when the guest first boots:
|
||||||
|
|
||||||
|
virt-builder fedora-20 --firstboot-install inkscape
|
||||||
|
|
||||||
|
This uses the guest's package manager and the guest's network
|
||||||
|
connection.
|
||||||
|
|
||||||
|
The downsides are that it will take the guest a lot longer to boot
|
||||||
|
first time, and there's nothing much you can do if package
|
||||||
|
installation fails (eg. because a network problem means the guest
|
||||||
|
can't reach the package repositories).
|
||||||
|
|
||||||
|
=head3 Installing packages at build time from a side repository
|
||||||
|
|
||||||
|
If the software you want to install is not available in the main
|
||||||
|
package repository of the guest, then you can add a side repository.
|
||||||
|
Usually this is presented as an ISO (CD disk image) file containing
|
||||||
|
extra packages.
|
||||||
|
|
||||||
|
Create a script that mounts the ISO and sets up the repository. For
|
||||||
|
yum, create /tmp/install.sh containing:
|
||||||
|
|
||||||
|
mkdir /tmp/mount
|
||||||
|
|
||||||
|
# Assume the volume label of the CD is 'EXTRA':
|
||||||
|
mount LABEL=EXTRA /tmp/mount
|
||||||
|
|
||||||
|
cat <<'EOF' > /etc/yum.repos.d/extra.repo
|
||||||
|
[extra]
|
||||||
|
name=extra
|
||||||
|
baseurl=file:///tmp/mount
|
||||||
|
enabled=1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
yum -y install famousdatabase
|
||||||
|
|
||||||
|
For apt, create /tmp/install.sh containing:
|
||||||
|
|
||||||
|
mkdir /tmp/mount
|
||||||
|
|
||||||
|
# Assume the volume label of the CD is 'EXTRA':
|
||||||
|
mount LABEL=EXTRA /tmp/mount
|
||||||
|
|
||||||
|
apt-cdrom -d=/tmp/mount add
|
||||||
|
apt-get -y install famousdatabase
|
||||||
|
|
||||||
|
Use the I<--attach> option to attach the CD:
|
||||||
|
|
||||||
|
virt-builder fedora 20 --attach extra.iso --run /tmp/install.sh
|
||||||
|
|
||||||
|
=head2 USERS AND PASSWORDS
|
||||||
|
|
||||||
|
The I<--root-password> option is used to change the root password
|
||||||
|
(otherwise a random password is used). This option has the following
|
||||||
|
formats:
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item B<--root-password> file:FILENAME
|
||||||
|
|
||||||
|
Read the root password from C<FILENAME>. The whole first line
|
||||||
|
of this file is the replacement password. Any other lines are
|
||||||
|
ignored. You should create the file with mode 0600 to ensure
|
||||||
|
no one else can read it.
|
||||||
|
|
||||||
|
=item B<--root-password> password:PASSWORD
|
||||||
|
|
||||||
|
Set the root password to the literal string C<PASSWORD>.
|
||||||
|
|
||||||
|
B<Note: this is not secure> since any user on the same machine can
|
||||||
|
see the cleartext password using L<ps(1)>.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head3 Creating user accounts
|
||||||
|
|
||||||
|
To create user accounts, use the L<useradd(8)> command with
|
||||||
|
L<--firstboot-command> like this:
|
||||||
|
|
||||||
|
virt-sysprep --firstboot-command \
|
||||||
|
'useradd -m -p "" rjones ; chage -d 0 rjones'
|
||||||
|
|
||||||
|
The above command will create an C<rjones> account with no password,
|
||||||
|
and force the user to set a password when they first log in. There
|
||||||
|
are other ways to manage passwords, see L<useradd(8)> for details.
|
||||||
|
|
||||||
|
=head2 INSTALLATION PROCESS
|
||||||
|
|
||||||
|
When you invoke virt-builder, installation proceeds as follows:
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
The template image is downloaded.
|
||||||
|
|
||||||
|
If the template image is present in the cache, the cached version
|
||||||
|
is used instead. (See L</CACHING>).
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
The template signature is checked.
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
If the template image is xz-compressed: If L<nbdkit(1)> and
|
||||||
|
L<nbdkit-xz-plugin(1)> are both installed, nbdkit is used to
|
||||||
|
transparently uncompress the image on the fly. Else it is
|
||||||
|
uncompressed to a temporary disk which takes more disk space.
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
The template image is resized into the destination, using
|
||||||
|
L<virt-resize(1)>.
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
Extra disks are attached (I<--attach>).
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
A new random seed is generated for the guest.
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
Packages are installed (I<--install>).
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
The root password is changed (I<--root-password>).
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
Files are uploaded (I<--upload>).
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
Firstboot scripts are installed (I<--firstboot>,
|
||||||
|
I<--firstboot-command>, I<--firstboot-install>).
|
||||||
|
|
||||||
|
Note that although firstboot scripts are installed at this step, they
|
||||||
|
do not run until the guest is booted first time. Firstboot scripts
|
||||||
|
will run in the order they appear on the command line.
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
Scripts are run (I<--run>, I<--run-command>).
|
||||||
|
|
||||||
|
Scripts run in the order they appear on the command line.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head2 CREATING YOUR OWN TEMPLATES
|
||||||
|
|
||||||
|
For serious virt-builder use, you may want to create your own
|
||||||
|
repository of templates.
|
||||||
|
|
||||||
|
Out of the box, virt-builder downloads the file
|
||||||
|
L<http://libguestfs.org/download/builder/index.asc> which is an index
|
||||||
|
of available templates plus some information about each one, wrapped
|
||||||
|
up in a digital signature. The command C<virt-builder --list> lists
|
||||||
|
out the information in this index file.
|
||||||
|
|
||||||
|
You can set up your own site containing an index file and some
|
||||||
|
templates, and then point virt-builder at the site by using the
|
||||||
|
I<--source> option:
|
||||||
|
|
||||||
|
virt-builder --source https://example.com/builder/index.asc \
|
||||||
|
--fingerprint 'AAAA BBBB ...' \
|
||||||
|
--list
|
||||||
|
|
||||||
|
(Note setting the environment variables C<VIRT_BUILDER_SOURCE> and
|
||||||
|
C<VIRT_BUILDER_FINGERPRINT> may be easier to type!)
|
||||||
|
|
||||||
|
=head3 Setting up a GPG key
|
||||||
|
|
||||||
|
If you don't have a GnuPG key, you will need to set one up. (Strictly
|
||||||
|
speaking this is optional, but if your index and template files are
|
||||||
|
not signed then virt-builder users will have to use the
|
||||||
|
I<--no-check-signature> flag every time they use virt-builder.)
|
||||||
|
|
||||||
|
To create a key, see the GPG manual
|
||||||
|
L<http://www.gnupg.org/gph/en/manual.html>.
|
||||||
|
|
||||||
|
Export your GPG public key and add it to the keyring of all
|
||||||
|
virt-builder users:
|
||||||
|
|
||||||
|
gpg --export -a "you@example.com" > pubkey
|
||||||
|
|
||||||
|
# For each virt-builder user:
|
||||||
|
gpg --import pubkey
|
||||||
|
|
||||||
|
Also find the fingerprint of your key:
|
||||||
|
|
||||||
|
gpg --list-keys --fingerprint
|
||||||
|
|
||||||
|
=head3 Create the templates
|
||||||
|
|
||||||
|
There are many ways to create the templates. For example you could
|
||||||
|
clone existing guests (see L<virt-sysprep(1)>), or you could install a
|
||||||
|
guest by hand (L<virt-install(1)>). To see how the templates were
|
||||||
|
created for virt-builder, look at the scripts in
|
||||||
|
C<libguestfs.git/builder/website>
|
||||||
|
|
||||||
|
For best results when compressing the templates, use the following xz
|
||||||
|
options (see L<nbdkit-xz-plugin(1)> for further explanation):
|
||||||
|
|
||||||
|
xz --best --block-size=16777216 disk
|
||||||
|
|
||||||
|
=head3 Creating and signing the index file
|
||||||
|
|
||||||
|
The index file has a simple text format (shown here without the
|
||||||
|
digital signature):
|
||||||
|
|
||||||
|
[fedora-18]
|
||||||
|
name=Fedora® 18
|
||||||
|
osinfo=fedora18
|
||||||
|
file=fedora-18.xz
|
||||||
|
sig=fedora-18.xz.sig
|
||||||
|
format=raw
|
||||||
|
size=6442450944
|
||||||
|
compressed_size=148947524
|
||||||
|
expand=/dev/sda3
|
||||||
|
|
||||||
|
[fedora-19]
|
||||||
|
name=Fedora® 19
|
||||||
|
osinfo=fedora19
|
||||||
|
file=fedora-19.xz
|
||||||
|
sig=fedora-19.xz.sig
|
||||||
|
revision=3
|
||||||
|
format=raw
|
||||||
|
size=4294967296
|
||||||
|
compressed_size=172190964
|
||||||
|
expand=/dev/sda3
|
||||||
|
|
||||||
|
The part in square brackets is the C<os-version>, which is the same
|
||||||
|
string that is used on the virt-builder command line to build that OS.
|
||||||
|
|
||||||
|
After preparing the C<index> file in the correct format, clearsign it
|
||||||
|
using the following command:
|
||||||
|
|
||||||
|
gpg --clearsign --armor index
|
||||||
|
|
||||||
|
This will create the final file called C<index.asc> which can be
|
||||||
|
uploaded to the server (and is the I<--source> URL). As noted above,
|
||||||
|
signing the index file is optional, but recommended.
|
||||||
|
|
||||||
|
The following fields can appear:
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item C<name=NAME>
|
||||||
|
|
||||||
|
The user-friendly name of this template. This is displayed in the
|
||||||
|
I<--list> output but is otherwise not significant.
|
||||||
|
|
||||||
|
=item C<osinfo=ID>
|
||||||
|
|
||||||
|
This optional field maps the operating system to the associated
|
||||||
|
libosinfo ID. Virt-builder does not use it (yet).
|
||||||
|
|
||||||
|
=item C<file=PATH>
|
||||||
|
|
||||||
|
The path (relative to the index) of the xz-compressed template.
|
||||||
|
|
||||||
|
Note that absolute paths or URIs are B<not> permitted here. This is
|
||||||
|
because virt-builder has a "same origin" policy for templates so they
|
||||||
|
cannot come from other servers.
|
||||||
|
|
||||||
|
=item C<sig=PATH>
|
||||||
|
|
||||||
|
The path (relative to the index) of the GPG detached signature of the
|
||||||
|
xz file.
|
||||||
|
|
||||||
|
Note that absolute paths or URIs are B<not> permitted here. This is
|
||||||
|
because virt-builder has a "same origin" policy for templates so they
|
||||||
|
cannot come from other servers.
|
||||||
|
|
||||||
|
The file can be created as follows:
|
||||||
|
|
||||||
|
gpg --detach-sign --armor -o disk.xz.sig disk.xz
|
||||||
|
|
||||||
|
The signature is optional, but if you don't have it then virt-builder
|
||||||
|
users will need to use the I<--no-check-signature> option in order
|
||||||
|
to install from this template.
|
||||||
|
|
||||||
|
=item C<revision=N>
|
||||||
|
|
||||||
|
The revision is an integer which is used to control the template
|
||||||
|
cache. Increasing the revision number causes clients to download the
|
||||||
|
template again even if they have a copy in the cache.
|
||||||
|
|
||||||
|
The revision number is optional. If omitted it defaults to C<1>.
|
||||||
|
|
||||||
|
=item C<format=raw>
|
||||||
|
|
||||||
|
=item C<format=qcow2>
|
||||||
|
|
||||||
|
Specify the format of the disk image (before it was compressed). If
|
||||||
|
not given, the format is autodetected, but generally it is better to
|
||||||
|
be explicit about the intended format.
|
||||||
|
|
||||||
|
Note this is the source format, which is different from the
|
||||||
|
I<--format> option (requested output format). Virt-builder does
|
||||||
|
on-the-fly conversion from the source format to the requested output
|
||||||
|
format.
|
||||||
|
|
||||||
|
=item C<size=NNN>
|
||||||
|
|
||||||
|
The virtual size of the image in bytes. This is the size of the image
|
||||||
|
when uncompressed. If using a non-raw format such as qcow2 then it
|
||||||
|
means the virtual disk size, not the size of the qcow2 file.
|
||||||
|
|
||||||
|
This field is required.
|
||||||
|
|
||||||
|
Virt-builder also uses this as the minimum size that users can request
|
||||||
|
via the I<--size> option, or as the default size if there is no
|
||||||
|
I<--size> option.
|
||||||
|
|
||||||
|
=item C<compressed_size=NNN>
|
||||||
|
|
||||||
|
The compressed size of the disk image in bytes. This is just used for
|
||||||
|
information (when using I<--list --long>).
|
||||||
|
|
||||||
|
=item C<expand=/dev/sdaX>
|
||||||
|
|
||||||
|
When expanding the image to its final size, instruct L<virt-resize(1)>
|
||||||
|
to expand the named partition in the guest image to fill up all
|
||||||
|
available space. This works like the virt-resize I<--expand> option.
|
||||||
|
|
||||||
|
You should usually put the device name of the guest's root filesystem here.
|
||||||
|
|
||||||
|
It's a good idea to use this, but not required. If the field is
|
||||||
|
omitted then virt-resize will create an extra partition at the end of
|
||||||
|
the disk to cover the free space, which is much less user-friendly.
|
||||||
|
|
||||||
|
=item C<lvexpand=/dev/VolGroup/LogVol>
|
||||||
|
|
||||||
|
When expanding the image to its final size, instruct L<virt-resize(1)>
|
||||||
|
to expand the named logical volume in the guest image to fill up all
|
||||||
|
available space. This works like the virt-resize I<--lv-expand> option.
|
||||||
|
|
||||||
|
If the guest uses LVM2 you should usually put the LV of the guest's
|
||||||
|
root filesystem here. If the guest does not use LVM2 or its root
|
||||||
|
filesystem is not on an LV, don't use this option.
|
||||||
|
|
||||||
|
=item C<notes=NOTES>
|
||||||
|
|
||||||
|
Any notes that go with this image, especially notes describing what
|
||||||
|
packages are in the image, how the image was prepared, and licensing
|
||||||
|
information.
|
||||||
|
|
||||||
|
You can use multi-line notes here by indenting each new line with at
|
||||||
|
least one character of whitespace (even on blank lines):
|
||||||
|
|
||||||
|
notes=This image was prepared using
|
||||||
|
the following kickstart script:
|
||||||
|
<-- one space at beginning of line
|
||||||
|
timezone Europe/London
|
||||||
|
part /boot --fstype ext3
|
||||||
|
|
||||||
|
=item C<hidden=true>
|
||||||
|
|
||||||
|
Using the hidden flag prevents the template from being listed by the
|
||||||
|
I<--list> option (but it is still installable). This is used for test
|
||||||
|
images.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head3 Running virt-builder against the alternate repository
|
||||||
|
|
||||||
|
Ensure each virt-builder user has imported your public key into
|
||||||
|
their gpg keyring (see above).
|
||||||
|
|
||||||
|
Each virt-builder user should export these environment variables:
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
C<VIRT_BUILDER_SOURCE> to point to the URL of the C<index.asc> file.
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
C<VIRT_BUILDER_FINGERPRINT> to contain the fingerprint (long hex
|
||||||
|
string) of the user who signed the index file and the templates.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
Now run virt-builder commands as normal, eg:
|
||||||
|
|
||||||
|
virt-builder --list --long
|
||||||
|
|
||||||
|
virt-builder os-version
|
||||||
|
|
||||||
|
To debug problems, add the C<-v> option to these commands.
|
||||||
|
|
||||||
|
=head3 Licensing of templates
|
||||||
|
|
||||||
|
You should be aware of the licensing of images that you distribute.
|
||||||
|
For open source guests, provide a link to the source code in the
|
||||||
|
C<notes> field and comply with other requirements (eg. around
|
||||||
|
trademarks).
|
||||||
|
|
||||||
|
=head2 CACHING
|
||||||
|
|
||||||
|
Since the templates are usually very large, downloaded templates are
|
||||||
|
cached in the user's home directory.
|
||||||
|
|
||||||
|
The location of the cache is C<$XDG_CACHE_HOME/virt-builder/> or
|
||||||
|
C<$HOME/.cache/virt-builder>. This directory can be deleted after use
|
||||||
|
if you want to save space by doing:
|
||||||
|
|
||||||
|
virt-builder --delete-cache
|
||||||
|
|
||||||
|
To disable the template cache, use I<--no-cache>.
|
||||||
|
|
||||||
|
Only templates are cached. The index and detached digital signatures
|
||||||
|
are not cached.
|
||||||
|
|
||||||
|
Virt-builder uses L<curl(1)> to download files. Since curl obeys
|
||||||
|
C<http_proxy> (etc) environment variables, files might also be cached
|
||||||
|
by your proxy if you have one.
|
||||||
|
|
||||||
|
=head2 DIGITAL SIGNATURES
|
||||||
|
|
||||||
|
Virt-builder uses GNU Privacy Guard (GnuPG or gpg) to verify that the
|
||||||
|
index and templates have not been tampered with.
|
||||||
|
|
||||||
|
The source points to an index file, which is optionally signed.
|
||||||
|
|
||||||
|
Virt-builder downloads the index and checks that the signature is
|
||||||
|
valid and the signer's fingerprint matches the specified fingerprint
|
||||||
|
(ie. I<--fingerprint>, C<VIRT_BUILDER_FINGERPRINT>, or a built-in
|
||||||
|
fingerprint, in that order).
|
||||||
|
|
||||||
|
For checking against the built-in public key/fingerprint, this
|
||||||
|
requires importing the public key into the user's local gpg keyring
|
||||||
|
(that's just the way that gpg works).
|
||||||
|
|
||||||
|
When a template is downloaded, its signature is checked in the same
|
||||||
|
way.
|
||||||
|
|
||||||
|
Although the signatures are optional, if you don't have them then
|
||||||
|
virt-builder users will have to use I<--no-check-signature> on the
|
||||||
|
command line. This prevents an attacker from replacing the signed
|
||||||
|
index file with an unsigned index file and having virt-builder
|
||||||
|
silently work without checking the signature. In any case it is
|
||||||
|
highly recommended that you always create signed index and templates.
|
||||||
|
|
||||||
|
=head1 ENVIRONMENT VARIABLES
|
||||||
|
|
||||||
|
For other environment variables which affect all libguestfs programs,
|
||||||
|
see L<guestfs(3)/ENVIRONMENT VARIABLES>.
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item C<http_proxy>
|
||||||
|
|
||||||
|
=item C<https_proxy>
|
||||||
|
|
||||||
|
=item C<no_proxy>
|
||||||
|
|
||||||
|
Set the proxy for downloads. These environment variables (and more)
|
||||||
|
are actually interpreted by L<curl(1)>, not virt-builder.
|
||||||
|
|
||||||
|
=item C<HOME>
|
||||||
|
|
||||||
|
Used to determine the location of the template cache. See L</CACHING>.
|
||||||
|
|
||||||
|
=item C<VIRT_BUILDER_FINGERPRINT>
|
||||||
|
|
||||||
|
Set the default value for the GPG signature fingerprint (see
|
||||||
|
I<--fingerprint> option).
|
||||||
|
|
||||||
|
=item C<VIRT_BUILDER_SOURCE>
|
||||||
|
|
||||||
|
Set the default value for the source URL for the template repository
|
||||||
|
(see I<--source> option).
|
||||||
|
|
||||||
|
=item C<XDG_CACHE_HOME>
|
||||||
|
|
||||||
|
Used to determine the location of the template cache. See L</CACHING>.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 EXIT STATUS
|
||||||
|
|
||||||
|
This program returns 0 if successful, or non-zero if there was an
|
||||||
|
error.
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<virt-resize(1)>,
|
||||||
|
L<virt-install(1)>,
|
||||||
|
L<virt-sysprep(1)>,
|
||||||
|
L<oz-install(1)>,
|
||||||
|
L<guestmount(1)>,
|
||||||
|
L<nbdkit(1)>,
|
||||||
|
L<nbdkit-xz-plugin(1)>,
|
||||||
|
L<gpg(1)>,
|
||||||
|
L<guestfs(3)>,
|
||||||
|
L<guestfish(1)>,
|
||||||
|
L<virt-copy-out(1)>,
|
||||||
|
L<curl(1)>,
|
||||||
|
L<http://libguestfs.org/>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Richard W.M. Jones L<http://people.redhat.com/~rjones/>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (C) 2013 Red Hat Inc.
|
||||||
1
builder/website/.gitignore
vendored
Normal file
1
builder/website/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.xz
|
||||||
42
builder/website/README
Normal file
42
builder/website/README
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
If you are looking at this file at http://libguestfs.org/download/builder
|
||||||
|
-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
This directory is used by the libguestfs 'virt-builder' to store the
|
||||||
|
clean, signed OS templates used for building new virtual machines.
|
||||||
|
|
||||||
|
The index file is the default source URL and links to the other OS
|
||||||
|
templates. It has the canonical URL:
|
||||||
|
|
||||||
|
http://libguestfs.org/download/builder/index.asc
|
||||||
|
|
||||||
|
If you are looking at this file in the git repository
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
libguestfs.git/builder/website/ contains a copy of the website, minus
|
||||||
|
the huge OS template files (because of their size, they are stored
|
||||||
|
elsewhere and merged into the website when it is uploaded).
|
||||||
|
|
||||||
|
When you use the ./run script to run virt-builder without installing,
|
||||||
|
the ./run script sets $VIRT_BUILDER_SOURCE to point to this directory.
|
||||||
|
If you actually want to use this configuration for anything except
|
||||||
|
simple testing, you will have to download one or more OS templates
|
||||||
|
from the libguestfs website and put them into the builder/website/
|
||||||
|
directory.
|
||||||
|
|
||||||
|
ie:
|
||||||
|
|
||||||
|
./run ./builder/virt-builder fedora-20
|
||||||
|
|
||||||
|
will fail unless you have downloaded fedora-20.xz here.
|
||||||
|
|
||||||
|
Fedora guests
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The general plan for using kickstart and virt-install is outlined
|
||||||
|
by Kashyap here:
|
||||||
|
|
||||||
|
http://kashyapc.wordpress.com/2011/08/18/unattended-guest-install-with-a-local-kickstart/
|
||||||
|
|
||||||
|
If you want to reproduce the builds then the kickstart files are
|
||||||
|
located in fedora-<N>.ks and the virt-install + other commands are in
|
||||||
|
fedora-<N>.sh.
|
||||||
38
builder/website/fedora-18.ks
Normal file
38
builder/website/fedora-18.ks
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# virt-builder
|
||||||
|
# Copyright (C) 2013 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.
|
||||||
|
|
||||||
|
install
|
||||||
|
text
|
||||||
|
reboot
|
||||||
|
lang en_US.UTF-8
|
||||||
|
keyboard us
|
||||||
|
network --bootproto dhcp
|
||||||
|
rootpw builder
|
||||||
|
firewall --enabled --ssh
|
||||||
|
selinux --enforcing
|
||||||
|
timezone --utc America/New_York
|
||||||
|
bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
|
||||||
|
zerombr
|
||||||
|
clearpart --all --initlabel
|
||||||
|
autopart --type=plain
|
||||||
|
|
||||||
|
# Halt the system once configuration has finished.
|
||||||
|
poweroff
|
||||||
|
|
||||||
|
%packages
|
||||||
|
@core
|
||||||
|
%end
|
||||||
75
builder/website/fedora-18.sh
Executable file
75
builder/website/fedora-18.sh
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/bash -
|
||||||
|
# virt-builder
|
||||||
|
# Copyright (C) 2013 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.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Some configuration.
|
||||||
|
export http_proxy=http://cache.home.annexia.org:3128
|
||||||
|
export https_proxy=$http_proxy
|
||||||
|
export ftp_proxy=$http_proxy
|
||||||
|
tree=http://mirror.bytemark.co.uk/fedora/linux/releases/18/Fedora/x86_64/os/
|
||||||
|
|
||||||
|
# Currently you have to run this script as root.
|
||||||
|
if [ `id -u` -ne 0 ]; then
|
||||||
|
echo "You have to run this script as root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure it's being run from the correct directory.
|
||||||
|
if [ ! -f fedora-18.ks ]; then
|
||||||
|
echo "You are running this script from the wrong directory."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
pwd=`pwd`
|
||||||
|
|
||||||
|
virsh undefine tmpf18 ||:
|
||||||
|
rm -f fedora-18 fedora-18.old
|
||||||
|
|
||||||
|
virt-install \
|
||||||
|
--name=tmpf18 \
|
||||||
|
--ram 2048 \
|
||||||
|
--cpu=host --vcpus=2 \
|
||||||
|
--os-type=linux --os-variant=fedora18 \
|
||||||
|
--initrd-inject=$pwd/fedora-18.ks \
|
||||||
|
--extra-args="ks=file:/fedora-18.ks console=tty0 console=ttyS0,115200 proxy=$http_proxy" \
|
||||||
|
--disk $pwd/fedora-18,size=6 \
|
||||||
|
--location=$tree \
|
||||||
|
--nographics \
|
||||||
|
--noreboot
|
||||||
|
# The virt-install command should exit after complete installation.
|
||||||
|
# Remove the guest, we don't want it to be defined in libvirt.
|
||||||
|
virsh undefine tmpf18
|
||||||
|
|
||||||
|
# Sysprep (removes logfiles and so on).
|
||||||
|
# Note this also touches /.autorelabel so the further installation
|
||||||
|
# changes that we make will be labelled properly at first boot.
|
||||||
|
virt-sysprep -a fedora-18
|
||||||
|
|
||||||
|
# Sparsify.
|
||||||
|
mv fedora-18 fedora-18.old
|
||||||
|
virt-sparsify fedora-18.old fedora-18
|
||||||
|
rm fedora-18.old
|
||||||
|
|
||||||
|
# Compress.
|
||||||
|
rm -f fedora-18.xz
|
||||||
|
xz --best --block-size=16777216 fedora-18
|
||||||
|
|
||||||
|
# Result:
|
||||||
|
ls -lh fedora-18.xz
|
||||||
17
builder/website/fedora-18.xz.sig
Normal file
17
builder/website/fedora-18.xz.sig
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: GnuPG v1.4.14 (GNU/Linux)
|
||||||
|
|
||||||
|
iQIcBAABAgAGBQJSSt3OAAoJEJFzj3Pht2ig8x4QAKoQVfMWxXiFvV3lrHUVsjpG
|
||||||
|
n+fDcoFJqMKeE/kMLhmlmzF+QDz1qRs3Xlypy7C8B5ii5jbFVCv6q5edeEWrVYE3
|
||||||
|
HZOeNgUgmVJEiCSgScEXPpVMAOL/zppJ2PjAbg7CNLHQ64HweNkv6F1ePg4NCIoA
|
||||||
|
Op5yIvzm0gUHN9ONfsJRBLlZGQRu8bdsNhdYOAr3rmdxhuuunIi/17qNrT6eaWFO
|
||||||
|
2pnGFIyYMn9q2ReXBG8mFIkHlac9ZpT2sR7EKY3LBt0FvGz0qQrNM+9M9bQBwbIQ
|
||||||
|
dD/IRfSUUQqjCLYLqiFvFVC/pqAAcR1G2rQ20jRLPkpSUFceFL/K5ueI2flEPEJk
|
||||||
|
mF6WR4MsN4lX4w0iiJvpWwE0jqlLVQ1GnhoE2GDZEHgkth/4l+0pN0Jos8OKINDJ
|
||||||
|
wB4W1Xc2aXNXaD1JAaebu+CthZNnFEXpa5TrXMFAOnBY4oQ4DgOt/ad1s9Ju4zLX
|
||||||
|
EI+Zn0Q++l+iMyU2InXnTHoaTagqKqtngvHWGmSuK55dM8jW9HoYsjZZl0oC0lVz
|
||||||
|
GbXQ/1t1loTBqh+crr4kcP0oKvRFT9YPASadUVThmeYlxOyhvQ0e30IBdz0Te1OR
|
||||||
|
rqfymVGR8NsGdLW+jCKOfbLEaYShTksTnUmr+yF5w5D2uzZHqZasE7cpwPXAEhuZ
|
||||||
|
TF7ZoSqz1utSQF9/xBnz
|
||||||
|
=L4x6
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
38
builder/website/fedora-19.ks
Normal file
38
builder/website/fedora-19.ks
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# virt-builder
|
||||||
|
# Copyright (C) 2013 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.
|
||||||
|
|
||||||
|
install
|
||||||
|
text
|
||||||
|
reboot
|
||||||
|
lang en_US.UTF-8
|
||||||
|
keyboard us
|
||||||
|
network --bootproto dhcp
|
||||||
|
rootpw builder
|
||||||
|
firewall --enabled --ssh
|
||||||
|
selinux --enforcing
|
||||||
|
timezone --utc America/New_York
|
||||||
|
bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
|
||||||
|
zerombr
|
||||||
|
clearpart --all --initlabel
|
||||||
|
autopart --type=plain
|
||||||
|
|
||||||
|
# Halt the system once configuration has finished.
|
||||||
|
poweroff
|
||||||
|
|
||||||
|
%packages
|
||||||
|
@core
|
||||||
|
%end
|
||||||
75
builder/website/fedora-19.sh
Executable file
75
builder/website/fedora-19.sh
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/bash -
|
||||||
|
# virt-builder
|
||||||
|
# Copyright (C) 2013 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.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Some configuration.
|
||||||
|
export http_proxy=http://cache.home.annexia.org:3128
|
||||||
|
export https_proxy=$http_proxy
|
||||||
|
export ftp_proxy=$http_proxy
|
||||||
|
tree=http://mirror.bytemark.co.uk/fedora/linux/releases/19/Fedora/x86_64/os/
|
||||||
|
|
||||||
|
# Currently you have to run this script as root.
|
||||||
|
if [ `id -u` -ne 0 ]; then
|
||||||
|
echo "You have to run this script as root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure it's being run from the correct directory.
|
||||||
|
if [ ! -f fedora-19.ks ]; then
|
||||||
|
echo "You are running this script from the wrong directory."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
pwd=`pwd`
|
||||||
|
|
||||||
|
virsh undefine tmpf19 ||:
|
||||||
|
rm -f fedora-19 fedora-19.old
|
||||||
|
|
||||||
|
virt-install \
|
||||||
|
--name=tmpf19 \
|
||||||
|
--ram 2048 \
|
||||||
|
--cpu=host --vcpus=2 \
|
||||||
|
--os-type=linux --os-variant=fedora19 \
|
||||||
|
--initrd-inject=$pwd/fedora-19.ks \
|
||||||
|
--extra-args="ks=file:/fedora-19.ks console=tty0 console=ttyS0,115200 proxy=$http_proxy" \
|
||||||
|
--disk $pwd/fedora-19,size=4 \
|
||||||
|
--location=$tree \
|
||||||
|
--nographics \
|
||||||
|
--noreboot
|
||||||
|
# The virt-install command should exit after complete installation.
|
||||||
|
# Remove the guest, we don't want it to be defined in libvirt.
|
||||||
|
virsh undefine tmpf19
|
||||||
|
|
||||||
|
# Sysprep (removes logfiles and so on).
|
||||||
|
# Note this also touches /.autorelabel so the further installation
|
||||||
|
# changes that we make will be labelled properly at first boot.
|
||||||
|
virt-sysprep -a fedora-19
|
||||||
|
|
||||||
|
# Sparsify.
|
||||||
|
mv fedora-19 fedora-19.old
|
||||||
|
virt-sparsify fedora-19.old fedora-19
|
||||||
|
rm fedora-19.old
|
||||||
|
|
||||||
|
# Compress.
|
||||||
|
rm -f fedora-19.xz
|
||||||
|
xz --best --block-size=16777216 fedora-19
|
||||||
|
|
||||||
|
# Result:
|
||||||
|
ls -lh fedora-19.xz
|
||||||
17
builder/website/fedora-19.xz.sig
Normal file
17
builder/website/fedora-19.xz.sig
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: GnuPG v1.4.14 (GNU/Linux)
|
||||||
|
|
||||||
|
iQIcBAABAgAGBQJSSt3WAAoJEJFzj3Pht2igSeMQAI42FK1YU6Fr/LreD9/BdjNH
|
||||||
|
TUJ3L/B3ZMJwlMT+F4cX9r1uVD5udzpXdClUEcKgdwPdqzPsQvuwLSBS4fn38nZM
|
||||||
|
mryzNeUjbcNPSPSY6bj/MNuYNlYNaWShhL6UjHBD6XYIhAs6hYp0RGcogcpgRU9Z
|
||||||
|
0peHFyJBp+uUqCpnBDz00zJht49mgT6ovUA3cGd/vPhHrsrJUusgzzi46X3F90+H
|
||||||
|
9eRoe6/xzCbmbiD7HUlARnMK8ec0b7H/CWsNNCd0v9KBNyW9QROkbwBMtRcWtQZW
|
||||||
|
p9h9JGT2N3AmIqRbUbKwfWxGN6AfK+Xtl9YjJb/ugwSlw0Pif055EKNeUZjGUUzk
|
||||||
|
V4YA9b1Z6mncyL4wxV8VD9w8en+dr5Iu6ga02r7Xr0rIHuTQyQQaKfHvNoWZFmhq
|
||||||
|
WI2xsSBLr1EYfLEetdqUkEo2f2gpy7f6UKL1bsHi/6oqA3NaOOXiPdVlbVDNOQFe
|
||||||
|
kXPfjz/8hnzQ/8O/zIiSiDefQObdM4DQDTk5ha/vOu4pt5XevHkusaciPNGUAhx1
|
||||||
|
JmxEmPF6sqg2Y0xmeFZB2ab5s6VL/CfVGBF5ZFq7QmDj9eNFjrElfv+uN9NqD+ll
|
||||||
|
YlR/g3f7vFLHSG64ez4yV/Hgmfv1+4DMGi9MNmhFF3u0W5AIxznTsQzj1KFhbvmV
|
||||||
|
8QUyTDG7lGNqsgsSgb7W
|
||||||
|
=l9GJ
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
35
builder/website/index
Normal file
35
builder/website/index
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
[fedora-18]
|
||||||
|
name=Fedora® 18
|
||||||
|
osinfo=fedora18
|
||||||
|
file=fedora-18.xz
|
||||||
|
sig=fedora-18.xz.sig
|
||||||
|
format=raw
|
||||||
|
size=6442450944
|
||||||
|
compressed_size=148947524
|
||||||
|
expand=/dev/sda3
|
||||||
|
notes=This Fedora image contains only unmodified @Core group packages.
|
||||||
|
It is thus very minimal. The kickstart and install script can be
|
||||||
|
found in the libguestfs git tree:
|
||||||
|
libguestfs.git/builder/website/fedora-18.ks
|
||||||
|
libguestfs.git/builder/website/fedora-18.sh
|
||||||
|
|
||||||
|
Fedora and the Infinity design logo are trademarks of Red Hat, Inc.
|
||||||
|
Source and further information is available from http://fedoraproject.org/
|
||||||
|
|
||||||
|
[fedora-19]
|
||||||
|
name=Fedora® 19
|
||||||
|
osinfo=fedora19
|
||||||
|
file=fedora-19.xz
|
||||||
|
sig=fedora-19.xz.sig
|
||||||
|
format=raw
|
||||||
|
size=4294967296
|
||||||
|
compressed_size=172190964
|
||||||
|
expand=/dev/sda3
|
||||||
|
notes=This Fedora image contains only unmodified @Core group packages.
|
||||||
|
It is thus very minimal. The kickstart and install script can be
|
||||||
|
found in the libguestfs git tree:
|
||||||
|
libguestfs.git/builder/website/fedora-19.ks
|
||||||
|
libguestfs.git/builder/website/fedora-19.sh
|
||||||
|
|
||||||
|
Fedora and the Infinity design logo are trademarks of Red Hat, Inc.
|
||||||
|
Source and further information is available from http://fedoraproject.org/
|
||||||
55
builder/website/index.asc
Normal file
55
builder/website/index.asc
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
|
Hash: SHA1
|
||||||
|
|
||||||
|
[fedora-18]
|
||||||
|
name=Fedora® 18
|
||||||
|
osinfo=fedora18
|
||||||
|
file=fedora-18.xz
|
||||||
|
sig=fedora-18.xz.sig
|
||||||
|
format=raw
|
||||||
|
size=6442450944
|
||||||
|
compressed_size=148947524
|
||||||
|
expand=/dev/sda3
|
||||||
|
notes=This Fedora image contains only unmodified @Core group packages.
|
||||||
|
It is thus very minimal. The kickstart and install script can be
|
||||||
|
found in the libguestfs git tree:
|
||||||
|
libguestfs.git/builder/website/fedora-18.ks
|
||||||
|
libguestfs.git/builder/website/fedora-18.sh
|
||||||
|
|
||||||
|
Fedora and the Infinity design logo are trademarks of Red Hat, Inc.
|
||||||
|
Source and further information is available from http://fedoraproject.org/
|
||||||
|
|
||||||
|
[fedora-19]
|
||||||
|
name=Fedora® 19
|
||||||
|
osinfo=fedora19
|
||||||
|
file=fedora-19.xz
|
||||||
|
sig=fedora-19.xz.sig
|
||||||
|
format=raw
|
||||||
|
size=4294967296
|
||||||
|
compressed_size=172190964
|
||||||
|
expand=/dev/sda3
|
||||||
|
notes=This Fedora image contains only unmodified @Core group packages.
|
||||||
|
It is thus very minimal. The kickstart and install script can be
|
||||||
|
found in the libguestfs git tree:
|
||||||
|
libguestfs.git/builder/website/fedora-19.ks
|
||||||
|
libguestfs.git/builder/website/fedora-19.sh
|
||||||
|
|
||||||
|
Fedora and the Infinity design logo are trademarks of Red Hat, Inc.
|
||||||
|
Source and further information is available from http://fedoraproject.org/
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: GnuPG v1.4.14 (GNU/Linux)
|
||||||
|
|
||||||
|
iQIcBAEBAgAGBQJSSuCLAAoJEJFzj3Pht2igR3kP/12WWghoeAebywPbRoY0gERN
|
||||||
|
WvYxYoE5YiFgKnsjEOCuTw5S+0X69sKmUbW/I6eCVOVon/VDJqHNto7IPF3eYL9J
|
||||||
|
yHSHHOu3PBwzkwXevCYpn+ji6pMw8BFBm08JxwGnLNoTh/1OKTb1RqP2XLepJ/7F
|
||||||
|
c9AhGhRRo7f0hHHVcuceK3IV4BfKiXCPBvAeCmnHPwc1onMtsex9ztXohjw7N+7m
|
||||||
|
I7y85K9GJ1gA0fzPuWElBpeDvZlRiINqXLoDSM+5X0AVcPXspFBhT42Tos/N50nW
|
||||||
|
0SXZgwgp5hLxn/6ytS8UCZqgerWQsJz5YooHuUQ8LKzXUqRYUz7YXkW60nq9VWj9
|
||||||
|
zS0e6xpasAw+8JukrY/fFZaxx3PDbqFOA/pD3kioQBUNwl2VJl5z58vKD1FEx7EL
|
||||||
|
HRa8/EB8xlU5dj8bMZ3VKGefYEq6b3iLskhQYVNgzOYoK5k43LPXZAJ0krFGNUrG
|
||||||
|
UH5JM7hd87xzoMvzIlJQ1T7dCzCyAVrDnUOT8mUAPQJcJIAoMXLqGRnG19odwOfh
|
||||||
|
SvJ1TnK70SxSxFji4q3XpsTBH+1CZi2l4suju0bXQkfaWhwj9SXDhnwhJR06CkZO
|
||||||
|
nPFD1MJZuqepitMgV0y4l9THn84piNzKHx9LkZJPeq31vPIJHSCxUFEiAG9c3KU2
|
||||||
|
kHaqUU4zIWlxaO/yHQmc
|
||||||
|
=aPYy
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
@@ -1639,6 +1639,7 @@ AC_CONFIG_FILES([Makefile
|
|||||||
align/Makefile
|
align/Makefile
|
||||||
appliance/Makefile
|
appliance/Makefile
|
||||||
bash/Makefile
|
bash/Makefile
|
||||||
|
builder/Makefile
|
||||||
cat/Makefile
|
cat/Makefile
|
||||||
csharp/Makefile
|
csharp/Makefile
|
||||||
daemon/Makefile
|
daemon/Makefile
|
||||||
|
|||||||
@@ -1557,6 +1557,7 @@ a different filename by using the C<filename=> prefix.
|
|||||||
L<guestfs(3)>,
|
L<guestfs(3)>,
|
||||||
L<http://libguestfs.org/>,
|
L<http://libguestfs.org/>,
|
||||||
L<virt-alignment-scan(1)>,
|
L<virt-alignment-scan(1)>,
|
||||||
|
L<virt-builder(1)>,
|
||||||
L<virt-cat(1)>,
|
L<virt-cat(1)>,
|
||||||
L<virt-copy-in(1)>,
|
L<virt-copy-in(1)>,
|
||||||
L<virt-copy-out(1)>,
|
L<virt-copy-out(1)>,
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ OBJECTS = \
|
|||||||
progress-c.o \
|
progress-c.o \
|
||||||
uri-c.o \
|
uri-c.o \
|
||||||
crypt-c.o \
|
crypt-c.o \
|
||||||
|
libdir.cmx \
|
||||||
common_gettext.cmx \
|
common_gettext.cmx \
|
||||||
common_utils.cmx \
|
common_utils.cmx \
|
||||||
random_seed.cmx \
|
random_seed.cmx \
|
||||||
@@ -101,6 +102,13 @@ dummy: $(OBJECTS)
|
|||||||
.ml.cmx:
|
.ml.cmx:
|
||||||
$(OCAMLFIND) ocamlopt $(OCAMLOPTFLAGS) -c $< -o $@
|
$(OCAMLFIND) ocamlopt $(OCAMLOPTFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
# This OCaml module has to be generated by make (configure will put
|
||||||
|
# unexpanded prefix macro in).
|
||||||
|
|
||||||
|
libdir.ml: Makefile
|
||||||
|
echo 'let libdir = "$(libdir)"' > $@-t
|
||||||
|
mv $@-t $@
|
||||||
|
|
||||||
# automake will decide we don't need C support in this file. Really
|
# automake will decide we don't need C support in this file. Really
|
||||||
# we do, so we have to provide it ourselves.
|
# we do, so we have to provide it ourselves.
|
||||||
|
|
||||||
|
|||||||
@@ -146,6 +146,37 @@ let string_random8 =
|
|||||||
) [1;2;3;4;5;6;7;8]
|
) [1;2;3;4;5;6;7;8]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(* Drop elements from a list while a predicate is true. *)
|
||||||
|
let rec dropwhile f = function
|
||||||
|
| [] -> []
|
||||||
|
| x :: xs when f x -> dropwhile f xs
|
||||||
|
| xs -> xs
|
||||||
|
|
||||||
|
(* Take elements from a list while a predicate is true. *)
|
||||||
|
let rec takewhile f = function
|
||||||
|
| x :: xs when f x -> x :: takewhile f xs
|
||||||
|
| _ -> []
|
||||||
|
|
||||||
|
let rec filter_map f = function
|
||||||
|
| [] -> []
|
||||||
|
| x :: xs ->
|
||||||
|
match f x with
|
||||||
|
| Some y -> y :: filter_map f xs
|
||||||
|
| None -> filter_map f xs
|
||||||
|
|
||||||
|
(* Timestamped progress messages, used for ordinary messages when not
|
||||||
|
* --quiet.
|
||||||
|
*)
|
||||||
|
let start_t = Unix.time ()
|
||||||
|
let make_message_function ~quiet fs =
|
||||||
|
let p str =
|
||||||
|
if not quiet then (
|
||||||
|
let t = sprintf "%.1f" (Unix.time () -. start_t) in
|
||||||
|
printf "[%8s] %s\n%!" t str
|
||||||
|
)
|
||||||
|
in
|
||||||
|
ksprintf p fs
|
||||||
|
|
||||||
let error ~prog fs =
|
let error ~prog fs =
|
||||||
let display str =
|
let display str =
|
||||||
wrap ~chan:stderr (sprintf (f_"%s: error: %s") prog str);
|
wrap ~chan:stderr (sprintf (f_"%s: error: %s") prog str);
|
||||||
@@ -175,6 +206,26 @@ let read_whole_file path =
|
|||||||
close_in chan;
|
close_in chan;
|
||||||
Buffer.contents buf
|
Buffer.contents buf
|
||||||
|
|
||||||
|
(* Parse a size field, eg. "10G". *)
|
||||||
|
let parse_size =
|
||||||
|
let const_re = Str.regexp "^\\([.0-9]+\\)\\([bKMG]\\)$" in
|
||||||
|
fun ~prog field ->
|
||||||
|
let matches rex = Str.string_match rex field 0 in
|
||||||
|
let sub i = Str.matched_group i field in
|
||||||
|
let size_scaled f = function
|
||||||
|
| "b" -> Int64.of_float f
|
||||||
|
| "K" -> Int64.of_float (f *. 1024.)
|
||||||
|
| "M" -> Int64.of_float (f *. 1024. *. 1024.)
|
||||||
|
| "G" -> Int64.of_float (f *. 1024. *. 1024. *. 1024.)
|
||||||
|
| _ -> assert false
|
||||||
|
in
|
||||||
|
|
||||||
|
if matches const_re then (
|
||||||
|
size_scaled (float_of_string (sub 1)) (sub 2)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
error ~prog "%s: cannot parse size field" field
|
||||||
|
|
||||||
(* Parse a size field, eg. "10G", "+20%" etc. Used particularly by
|
(* Parse a size field, eg. "10G", "+20%" etc. Used particularly by
|
||||||
* virt-resize --resize and --resize-force options.
|
* virt-resize --resize and --resize-force options.
|
||||||
*)
|
*)
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ MANPAGES = \
|
|||||||
libguestfs-make-fixed-appliance.1 \
|
libguestfs-make-fixed-appliance.1 \
|
||||||
libguestfs-test-tool.1 \
|
libguestfs-test-tool.1 \
|
||||||
virt-alignment-scan.1 \
|
virt-alignment-scan.1 \
|
||||||
|
virt-builder.1 \
|
||||||
virt-cat.1 \
|
virt-cat.1 \
|
||||||
virt-copy-in.1 \
|
virt-copy-in.1 \
|
||||||
virt-copy-out.1 \
|
virt-copy-out.1 \
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
../align/virt-alignment-scan.pod
|
../align/virt-alignment-scan.pod
|
||||||
../appliance/libguestfs-make-fixed-appliance.pod
|
../appliance/libguestfs-make-fixed-appliance.pod
|
||||||
|
../builder/virt-builder.pod
|
||||||
../cat/virt-cat.pod
|
../cat/virt-cat.pod
|
||||||
../cat/virt-filesystems.pod
|
../cat/virt-filesystems.pod
|
||||||
../cat/virt-ls.pod
|
../cat/virt-ls.pod
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ MANPAGES = \
|
|||||||
libguestfs-make-fixed-appliance.1 \
|
libguestfs-make-fixed-appliance.1 \
|
||||||
libguestfs-test-tool.1 \
|
libguestfs-test-tool.1 \
|
||||||
virt-alignment-scan.1 \
|
virt-alignment-scan.1 \
|
||||||
|
virt-builder.1 \
|
||||||
virt-cat.1 \
|
virt-cat.1 \
|
||||||
virt-copy-in.1 \
|
virt-copy-in.1 \
|
||||||
virt-copy-out.1 \
|
virt-copy-out.1 \
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
|
builder/builder.ml
|
||||||
|
builder/downloader.ml
|
||||||
|
builder/get_kernel.ml
|
||||||
|
builder/index_parser.ml
|
||||||
|
builder/list_entries.ml
|
||||||
|
builder/sigchecker.ml
|
||||||
mllib/common_gettext.ml
|
mllib/common_gettext.ml
|
||||||
mllib/common_utils.ml
|
mllib/common_utils.ml
|
||||||
mllib/common_utils_tests.ml
|
mllib/common_utils_tests.ml
|
||||||
mllib/crypt.ml
|
mllib/crypt.ml
|
||||||
mllib/firstboot.ml
|
mllib/firstboot.ml
|
||||||
|
mllib/libdir.ml
|
||||||
mllib/password.ml
|
mllib/password.ml
|
||||||
mllib/progress.ml
|
mllib/progress.ml
|
||||||
mllib/random_seed.ml
|
mllib/random_seed.ml
|
||||||
|
|||||||
14
run.in
14
run.in
@@ -72,11 +72,12 @@ run: warning: has not been set automatically.
|
|||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Set the PATH to contain guestunmount (called by umount-local API).
|
# Set the PATH to contain guestunmount (called by umount-local API)
|
||||||
|
# and virt-resize (called by virt-builder).
|
||||||
if [ -z "$PATH" ]; then
|
if [ -z "$PATH" ]; then
|
||||||
PATH="$b/fuse"
|
PATH="$b/fuse:$b/resize"
|
||||||
else
|
else
|
||||||
PATH="$b/fuse:$PATH"
|
PATH="$b/fuse:$b/resize:$PATH"
|
||||||
fi
|
fi
|
||||||
export PATH
|
export PATH
|
||||||
|
|
||||||
@@ -88,6 +89,13 @@ else
|
|||||||
fi
|
fi
|
||||||
export LD_LIBRARY_PATH
|
export LD_LIBRARY_PATH
|
||||||
|
|
||||||
|
# Make virt-builder use the local website copy to avoid hitting
|
||||||
|
# the network all the time.
|
||||||
|
if [ -z "$VIRT_BUILDER_SOURCE" ]; then
|
||||||
|
VIRT_BUILDER_SOURCE="file://$s/builder/website/index.asc"
|
||||||
|
export VIRT_BUILDER_SOURCE
|
||||||
|
fi
|
||||||
|
|
||||||
# For Perl.
|
# For Perl.
|
||||||
if [ -z "$PERL5LIB" ]; then
|
if [ -z "$PERL5LIB" ]; then
|
||||||
PERL5LIB="$b/perl/blib/lib:$b/perl/blib/arch"
|
PERL5LIB="$b/perl/blib/lib:$b/perl/blib/arch"
|
||||||
|
|||||||
@@ -4210,6 +4210,10 @@ Bash tab-completion scripts.
|
|||||||
|
|
||||||
Various build scripts used by autotools.
|
Various build scripts used by autotools.
|
||||||
|
|
||||||
|
=item C<builder>
|
||||||
|
|
||||||
|
L<virt-builder(1)> command and documentation.
|
||||||
|
|
||||||
=item C<cat>
|
=item C<cat>
|
||||||
|
|
||||||
The L<virt-cat(1)>, L<virt-filesystems(1)> and L<virt-ls(1)> commands
|
The L<virt-cat(1)>, L<virt-filesystems(1)> and L<virt-ls(1)> commands
|
||||||
@@ -4661,6 +4665,7 @@ L<guestfs-ruby(3)>,
|
|||||||
L<guestfish(1)>,
|
L<guestfish(1)>,
|
||||||
L<guestmount(1)>,
|
L<guestmount(1)>,
|
||||||
L<virt-alignment-scan(1)>,
|
L<virt-alignment-scan(1)>,
|
||||||
|
L<virt-builder(1)>,
|
||||||
L<virt-cat(1)>,
|
L<virt-cat(1)>,
|
||||||
L<virt-copy-in(1)>,
|
L<virt-copy-in(1)>,
|
||||||
L<virt-copy-out(1)>,
|
L<virt-copy-out(1)>,
|
||||||
|
|||||||
Reference in New Issue
Block a user