virt-dib is a new tool to run the elements of diskimage-builder using
libguestfs.
---
Work in progress (debug stuff here and there), although I've submitting
it for initial review, for being included later when polished for good.
TODO items open:
- move Uname from builder to mllib
- improve the documentation
- review
- getting more testing (although it has been successfully tested for
a while, not only by myself)
- polish
.gitignore | 6 +
Makefile.am | 1 +
appliance/packagelist.in | 4 +
configure.ac | 3 +
dib/Makefile.am | 148 ++++++++
dib/cmdline.ml | 247 +++++++++++++
dib/dib.ml | 930 +++++++++++++++++++++++++++++++++++++++++++++++
dib/elements.ml | 219 +++++++++++
dib/link.sh.in | 22 ++
dib/uname-c.c | 57 +++
dib/uname.ml | 27 ++
dib/uname.mli | 28 ++
dib/utils.ml | 148 ++++++++
dib/virt-dib.pod | 541 +++++++++++++++++++++++++++
po-docs/podfiles | 1 +
run.in | 1 +
16 files changed, 2383 insertions(+)
create mode 100644 dib/Makefile.am
create mode 100644 dib/cmdline.ml
create mode 100644 dib/dib.ml
create mode 100644 dib/elements.ml
create mode 100644 dib/link.sh.in
create mode 100644 dib/uname-c.c
create mode 100644 dib/uname.ml
create mode 100644 dib/uname.mli
create mode 100644 dib/utils.ml
create mode 100644 dib/virt-dib.pod
diff --git a/.gitignore b/.gitignore
index 9c329a0..27aae29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,6 +120,11 @@ Makefile.in
/df/stamp-virt-df.pod
/df/virt-df
/df/virt-df.1
+/dib/.depend
+/dib/link.sh
+/dib/stamp-virt-dib.pod
+/dib/virt-dib
+/dib/virt-dib.1
/diff/stamp-virt-diff.pod
/diff/virt-diff
/diff/virt-diff.1
@@ -243,6 +248,7 @@ Makefile.in
/html/virt-copy-out.1.html
/html/virt-customize.1.html
/html/virt-df.1.html
+/html/virt-dib.1.html
/html/virt-diff.1.html
/html/virt-edit.1.html
/html/virt-filesystems.1.html
diff --git a/Makefile.am b/Makefile.am
index 580404a..c02d714 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -133,6 +133,7 @@ SUBDIRS += \
mllib \
customize \
builder builder/website \
+ dib \
resize \
sparsify \
sysprep \
diff --git a/appliance/packagelist.in b/appliance/packagelist.in
index 76c7293..2f6878e 100644
--- a/appliance/packagelist.in
+++ b/appliance/packagelist.in
@@ -257,3 +257,7 @@ ifelse(VALGRIND_DAEMON,1,valgrind)
dnl Define this by doing: ./configure --with-extra-packages="..."
EXTRA_PACKAGES
+curl
+qemu-img
+debootstrap
+apt
diff --git a/configure.ac b/configure.ac
index d4137f6..1f6d21c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1703,6 +1703,8 @@ AC_CONFIG_FILES([builder/link.sh],
[chmod +x,-w builder/link.sh])
AC_CONFIG_FILES([customize/link.sh],
[chmod +x,-w customize/link.sh])
+AC_CONFIG_FILES([dib/link.sh],
+ [chmod +x,-w dib/link.sh])
AC_CONFIG_FILES([inspector/test-xmllint.sh],
[chmod +x,-w inspector/test-xmllint.sh])
AC_CONFIG_FILES([mllib/link.sh],
@@ -1742,6 +1744,7 @@ AC_CONFIG_FILES([Makefile
customize/Makefile
daemon/Makefile
df/Makefile
+ dib/Makefile
diff/Makefile
edit/Makefile
erlang/Makefile
diff --git a/dib/Makefile.am b/dib/Makefile.am
new file mode 100644
index 0000000..b472a67
--- /dev/null
+++ b/dib/Makefile.am
@@ -0,0 +1,148 @@
+# libguestfs virt-dib tool
+# Copyright (C) 2015 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
+
+AM_YFLAGS = -d
+
+EXTRA_DIST = \
+ $(SOURCES_MLI) $(SOURCES_ML) $(SOURCES_C) \
+ virt-dib.pod
+
+CLEANFILES = *~ *.annot *.cmi *.cmo *.cmx *.cmxa *.o virt-dib
+
+SOURCES_MLI = \
+ uname.mli \
+ utils.mli
+
+SOURCES_ML = \
+ uname.ml \
+ utils.ml \
+ cmdline.ml \
+ elements.ml \
+ dib.ml
+
+SOURCES_C = \
+ $(top_srcdir)/mllib/mkdtemp-c.c \
+ uname-c.c
+
+bin_PROGRAMS =
+
+if HAVE_OCAML
+
+bin_PROGRAMS += virt-dib
+
+virt_dib_SOURCES = $(SOURCES_C)
+virt_dib_CPPFLAGS = \
+ -I. \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \
+ -I$(shell $(OCAMLC) -where) \
+ -I$(top_srcdir)/gnulib/lib \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/fish
+virt_dib_CFLAGS = \
+ -pthread \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS) \
+ -Wno-unused-macros
+
+BOBJECTS = \
+ $(top_builddir)/mllib/libdir.cmo \
+ $(top_builddir)/mllib/config.cmo \
+ $(top_builddir)/mllib/common_gettext.cmo \
+ $(top_builddir)/mllib/common_utils.cmo \
+ $(top_builddir)/mllib/mkdtemp.cmo \
+ $(SOURCES_ML:.ml=.cmo)
+XOBJECTS = $(BOBJECTS:.cmo=.cmx)
+
+# -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)/gnulib/lib/.libs \
+ -I $(top_builddir)/ocaml \
+ -I $(top_builddir)/mllib
+if HAVE_OCAML_PKG_GETTEXT
+OCAMLPACKAGES += -package gettext-stub
+endif
+
+OCAMLFLAGS = $(OCAML_FLAGS) $(OCAML_WARN_ERROR)
+
+if !HAVE_OCAMLOPT
+OBJECTS = $(BOBJECTS)
+BEST = c
+OCAMLLINKFLAGS = mlguestfs.cma -custom
+else
+OBJECTS = $(XOBJECTS)
+BEST = opt
+OCAMLLINKFLAGS = mlguestfs.cmxa
+endif
+
+virt_dib_DEPENDENCIES = $(OBJECTS)
+virt_dib_LINK = \
+ ./link.sh \
+ $(OCAMLFIND) $(BEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) $(OCAMLLINKFLAGS) \
+ $(OBJECTS) -o $@
+
+.mli.cmi:
+ $(OCAMLFIND) ocamlc $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
+.ml.cmo:
+ $(OCAMLFIND) ocamlc $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
+if HAVE_OCAMLOPT
+.ml.cmx:
+ $(OCAMLFIND) ocamlopt $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
+endif
+
+# Manual pages and HTML files for the website.
+
+man_MANS = virt-dib.1
+
+noinst_DATA = $(top_builddir)/html/virt-dib.1.html
+
+virt-dib.1 $(top_builddir)/html/virt-dib.1.html: stamp-virt-dib.pod
+
+stamp-virt-dib.pod: virt-dib.pod
+ $(PODWRAPPER) \
+ --man virt-dib.1 \
+ --html $(top_builddir)/html/virt-dib.1.html \
+ --license GPLv2+ \
+ $<
+ touch $@
+
+CLEANFILES += stamp-virt-dib.pod
+
+# Dependencies.
+depend: .depend
+
+.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml)
+ rm -f $@ $@-t
+ $(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/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
diff --git a/dib/cmdline.ml b/dib/cmdline.ml
new file mode 100644
index 0000000..9563260
--- /dev/null
+++ b/dib/cmdline.ml
@@ -0,0 +1,247 @@
+(* virt-dib
+ * Copyright (C) 2015 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.
+ *)
+
+(* Command line argument parsing. *)
+
+open Common_gettext.Gettext
+open Common_utils
+
+open Utils
+
+open Printf
+
+let parse_args ~prog =
+ let display_version () =
+ printf "%s %s\n" prog Config.package_version;
+ exit 0
+ in
+
+ let usage_msg =
+ sprintf (f_"\
+%s: run diskimage-builder elements to generate images
+
+ virt-dib -B DIB-LIB -p ELEMENTS-PATH elements...
+
+A short summary of the options is given below. For detailed help please
+read the man page virt-dib(1).
+")
+ prog in
+
+ let elements = ref [] in
+ let append_element element =
+ elements := element :: !elements in
+
+ let excluded_elements = ref [] in
+ let append_excluded_element element =
+ excluded_elements := element :: !excluded_elements in
+
+ let element_paths = ref [] in
+ let append_element_path arg =
+ element_paths := arg :: !element_paths in
+
+ let blacklist = ref [] in
+ let append_blacklisted_script arg =
+ blacklist := arg :: !blacklist in
+
+ let trace = ref false in
+ let verbose = ref false in
+ let debug = ref 0 in
+ let set_debug arg =
+ debug := arg in
+
+ let dryrun = ref false in
+
+ let basepath = ref "" in
+
+ let image_name = ref "image" in
+
+ let fs_type = ref "ext4" in
+
+ let size = ref (unit_GB 5) in
+ let set_size arg = size := parse_size ~prog arg in
+
+ let memsize = ref None in
+ let set_memsize arg = memsize := Some arg in
+
+ let network = ref true in
+
+ let smp = ref None in
+ let set_smp arg = smp := Some arg in
+
+ let formats = ref ["qcow2"] in
+ let set_format arg =
+ let fmts = string_nsplit "," arg in
+ let fmts = remove_dups fmts in
+ List.iter (
+ function
+ | "qcow2" | "tar" | "raw" -> ()
+ | fmt ->
+ error (f_"invalid format '%s' in --formats") fmt
+ ) fmts;
+ formats := fmts in
+
+ let envvars = ref [] in
+ let append_envvar arg =
+ envvars := arg :: !envvars in
+
+ let use_base = ref true in
+
+ let arch = ref "" in
+
+ let drive = ref None in
+ let set_drive arg = drive := Some arg in
+
+ let root_label = ref None in
+ let set_root_label arg = root_label := Some arg in
+
+ let install_type = ref "source" in
+
+ let image_cache = ref None in
+ let set_image_cache arg = image_cache := Some arg in
+
+ let compressed = ref true in
+
+ let delete_on_failure = ref true in
+
+ let is_ramdisk = ref false in
+ let ramdisk_element = ref "ramdisk" in
+
+ let qemu_img_options = ref None in
+ let set_qemu_img_options arg = qemu_img_options := Some arg in
+
+ let machine_readable = ref false in
+
+ let extra_packages = ref [] in
+ let append_extra_packages arg =
+ extra_packages := List.rev (string_nsplit "," arg) @ !extra_packages in
+
+ let argspec = [
+ "--short-options", Arg.Unit display_short_options, " " ^
s_"List short options";
+ "--long-options", Arg.Unit display_long_options, " " ^
s_"List long options";
+
+ "-p", Arg.String append_element_path, "path" ^ "
" ^ s_"Add new a elements location";
+ "--element-path", Arg.String append_element_path, "path" ^ "
" ^ s_"Add new a elements location";
+ "--exclude-element", Arg.String append_excluded_element,
+ "element" ^ " " ^ s_"Exclude the specified element";
+ "--blacklist", Arg.String append_blacklisted_script,
+ "script" ^ " " ^ s_"Blacklist (ignore) this script";
+ "--envvar", Arg.String append_envvar, "envvar[=value]" ^
" " ^ s_"Carry/set this environment variable";
+ "-n", Arg.Clear use_base, " " ^ s_"Skip the
inclusion of the 'base' element";
+ "--root-label", Arg.String set_root_label, "label" ^ "
" ^ s_"Label for the root fs";
+ "--install-type", Arg.Set_string install_type, "type" ^ "
" ^ s_"Installation type";
+ "--image-cache", Arg.String set_image_cache, "directory" ^ "
" ^ s_"Location for cached images";
+ "-u", Arg.Clear compressed, " " ^ "Do not
compress the qcow2 image";
+ "--qemu-img-options", Arg.String set_qemu_img_options,
+ "option" ^ " " ^
s_"Add qemu-img options";
+ "--extra-packages", Arg.String append_extra_packages,
+ "pkg,..." ^ " " ^ s_"Add extra packages to install";
+
+ "--ramdisk", Arg.Set is_ramdisk, " " ^ "Switch to
a ramdisk build";
+ "--ramdisk-element", Arg.Set_string ramdisk_element, "name" ^
" " ^ s_"Main element for building ramdisks";
+
+ "--name", Arg.Set_string image_name, "name" ^ " "
^ s_"Name of the image";
+ "--fs-type", Arg.Set_string fs_type, "fs" ^ " " ^
s_"Filesystem for the image";
+ "--size", Arg.String set_size, "size" ^ " "
^ s_"Set output disk size";
+ "--formats", Arg.String set_format, "qcow2,tgz,..." ^
" " ^ s_"Output formats";
+ "--arch", Arg.Set_string arch, "arch" ^ " "
^ s_"Output architecture";
+ "--drive", Arg.String set_drive, "path" ^ " "
^ s_"Optional drive for caches";
+
+ "-m",
Arg.Int set_memsize, "mb" ^ " " ^
s_"Set memory size";
+ "--memsize",
Arg.Int set_memsize, "mb" ^ " " ^
s_"Set memory size";
+ "--network", Arg.Set network, " " ^ s_"Enable
appliance network (default)";
+ "--no-network", Arg.Clear network, " " ^ s_"Disable
appliance network";
+ "--smp",
Arg.Int set_smp, "vcpus" ^ "
" ^ s_"Set number of vCPUs";
+ "--no-delete-on-failure", Arg.Clear delete_on_failure,
+ " " ^ s_"Don't delete
output file on failure";
+ "--machine-readable", Arg.Set machine_readable, " " ^
s_"Make output machine readable";
+
+ "-V", Arg.Unit display_version, " " ^ s_"Display
version and exit";
+ "--version", Arg.Unit display_version, " " ^ s_"Display
version and exit";
+ "-v", Arg.Set verbose, " " ^ s_"Enable
libguestfs debugging messages";
+ "--verbose", Arg.Set verbose, " " ^ s_"Enable
libguestfs debugging messages";
+ "-x", Arg.Set trace, " " ^ s_"Enable
tracing of libguestfs calls";
+ "--debug",
Arg.Int set_debug, "level" ^ "
" ^ s_"Set debug level";
+ "-d", Arg.Set dryrun, " " ^ s_"Set the
dry-run mode";
+ "-B", Arg.Set_string basepath, "path" ^ " "
^ s_"Base path of diskimage-builder library";
+ ] in
+
+ let argspec =
+ let cmp (arg1, _, _) (arg2, _, _) =
+ let arg1 = skip_dashes arg1 and arg2 = skip_dashes arg2 in
+ compare (String.lowercase arg1) (String.lowercase arg2)
+ in
+ List.sort cmp argspec in
+ let argspec = Arg.align argspec in
+ long_options := argspec;
+
+ Arg.parse argspec append_element usage_msg;
+
+ let verbose = !verbose in
+ let trace = !trace in
+ let debug = !debug in
+ let dryrun = !dryrun in
+ let basepath = !basepath in
+ let elements = List.rev !elements in
+ let excluded_elements = List.rev !excluded_elements in
+ let element_paths = List.rev !element_paths in
+ let blacklist = List.rev !blacklist in
+ let image_name = !image_name in
+ let fs_type = !fs_type in
+ let size = !size in
+ let memsize = !memsize in
+ let network = !network in
+ let smp = !smp in
+ let formats = !formats in
+ let envvars = !envvars in
+ let use_base = !use_base in
+ let arch = !arch in
+ let drive = !drive in
+ let root_label = !root_label in
+ let install_type = !install_type in
+ let image_cache = !image_cache in
+ let compressed = !compressed in
+ let delete_on_failure = !delete_on_failure in
+ let is_ramdisk = !is_ramdisk in
+ let ramdisk_element = !ramdisk_element in
+ let qemu_img_options = !qemu_img_options in
+ let machine_readable = !machine_readable in
+ let extra_packages = List.rev !extra_packages in
+
+ (* No elements and machine-readable mode? Print some facts. *)
+ if elements = [] && machine_readable then (
+ printf "virt-dib\n";
+ printf "output:qcow2\n";
+ printf "output:tar\n";
+ printf "output:raw\n";
+ exit 0
+ );
+
+ if basepath = "" then
+ error (f_"-B must be specified");
+
+ if formats = [] then
+ error (f_"the list of output formats cannot be empty");
+
+ if elements = [] then
+ error (f_"at least one distribution root element must be specified");
+
+ verbose, trace, debug, dryrun, basepath, elements, excluded_elements, element_paths,
+ blacklist, use_base, drive,
+ image_name, fs_type, size, root_label, install_type, image_cache, compressed,
+ qemu_img_options, is_ramdisk, ramdisk_element, extra_packages,
+ memsize, network, smp, delete_on_failure, formats, arch, envvars
diff --git a/dib/dib.ml b/dib/dib.ml
new file mode 100644
index 0000000..cf3fda6
--- /dev/null
+++ b/dib/dib.ml
@@ -0,0 +1,930 @@
+(* virt-dib
+ * Copyright (C) 2015 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 Cmdline
+open Utils
+open Elements
+
+open Printf
+open Unix
+
+module G = Guestfs
+
+let exclude_elements elements excluded_elements =
+ StringSet.filter (fun x -> List.mem x excluded_elements <> true) elements
+
+let read_envvars envvars =
+ filter_map (
+ fun var ->
+ let len = String.length var in
+ let i = string_find var "=" in
+ if i = -1 then (
+ try Some (var, Sys.getenv var)
+ with Not_found -> None
+ ) else (
+ Some (String.sub var 0 i, String.sub var (i + 1) (len - i - 1))
+ )
+ ) envvars
+
+let read_dib_envvars () =
+ let vars = Array.to_list (environment ()) in
+ let vars = List.filter (fun x -> string_prefix x "DIB_") vars in
+ List.fold_left (fun acc x -> acc ^ x ^ "\n") "" vars
+
+let make_dib_args args =
+ let args = Array.to_list args in
+ let rec quote_args = function
+ | [] -> ""
+ | x :: xs -> sprintf " %s" (quote x) ^ quote_args xs
+ in
+ match args with
+ | [] -> ""
+ | app :: xs -> app ^ quote_args xs
+
+let write_script fn text =
+ let oc = open_out fn in
+ output_string oc text;
+ flush oc;
+ close_out oc;
+ chmod fn 0o755
+
+let prepare_external ~dib_args ~dib_vars ~out_name ~root_label ~rootfs_uuid ~image_cache
+ ~arch ~network ~debug
+ destdir libdir hooksdir tmpdir fakebindir all_elements element_paths =
+ let network_string = if network then "" else "1" in
+
+ let run_extra = sprintf "\
+#!/bin/bash
+%s
+target_dir=$1
+shift
+script=$1
+shift
+
+export PATH=%s:$PATH
+
+# d-i-b variables
+export TMP_MOUNT_PATH=%s
+export DIB_OFFLINE=%s
+export IMAGE_NAME=\"%s\"
+export DIB_ROOT_LABEL=\"%s\"
+export DIB_IMAGE_ROOT_FS_UUID=%s
+export DIB_IMAGE_CACHE=\"%s\"
+export _LIB=%s
+export ARCH=%s
+export TMP_HOOKS_PATH=%s
+export DIB_ARGS=\"%s\"
+export IMAGE_ELEMENT=\"%s\"
+export ELEMENTS_PATH=\"%s\"
+export DIB_ENV=%s
+export TMPDIR=\"${TMP_MOUNT_PATH}/tmp\"
+export TMP_DIR=\"${TMPDIR}\"
+export DIB_DEBUG_TRACE=%d
+
+ENVIRONMENT_D_DIR=$target_dir/../environment.d
+
+if [ -d $ENVIRONMENT_D_DIR ] ; then
+ env_files=$(find $ENVIRONMENT_D_DIR -maxdepth 1 -xtype f | \
+ grep -E \"/[0-9A-Za-z_\\.-]+$\" | \
+ LANG=C sort -n)
+ for env_file in $env_files ; do
+ source $env_file
+ done
+fi
+
+$target_dir/$script
+"
+ (if debug >= 1 then "set -x\n" else "")
+ fakebindir
+ (quote tmpdir)
+ network_string
+ out_name
+ root_label
+ rootfs_uuid
+ image_cache
+ (quote libdir)
+ arch
+ (quote hooksdir)
+ dib_args
+ (String.concat " " (StringSet.elements all_elements))
+ (String.concat ":" element_paths)
+ (quote dib_vars)
+ debug in
+ write_script (destdir // "run-part-extra.sh") run_extra;
+
+ (* Needed as TMPDIR for the extra-data hooks *)
+ do_mkdir (tmpdir // "tmp")
+
+let prepare_aux ~envvars ~dib_args ~dib_vars ~log_file ~out_name ~rootfs_uuid ~arch
~network
+ ~root_label ~install_type ~debug ~extra_packages
+ destdir all_elements =
+ let envvars_string = List.map (
+ fun (var, value) ->
+ sprintf "export %s=%s" var (quote value)
+ ) envvars in
+ let network_string = if network then "" else "1" in
+
+ let script_run_part = sprintf "\
+#!/bin/bash
+%s
+sysroot=$1
+shift
+mysysroot=$1
+shift
+blockdev=$1
+shift
+target_dir=$1
+shift
+new_wd=$1
+shift
+script=$1
+shift
+
+# user variables
+%s
+
+# system variables
+export HOME=$mysysroot/tmp/aux/perm/home
+export PATH=$mysysroot/tmp/aux/hooks/bin:$PATH
+export TMP=$mysysroot/tmp
+export TMPDIR=$TMP
+export TMP_DIR=$TMP
+
+# d-i-b variables
+export TMP_MOUNT_PATH=$sysroot
+export TARGET_ROOT=$sysroot
+export DIB_OFFLINE=%s
+export IMAGE_NAME=\"%s\"
+export DIB_IMAGE_ROOT_FS_UUID=%s
+export DIB_IMAGE_CACHE=$HOME/.cache/image-create
+export DIB_ROOT_LABEL=\"%s\"
+export _LIB=$mysysroot/tmp/aux/lib
+export _PREFIX=$mysysroot/tmp/aux/elements
+export ARCH=%s
+export TMP_HOOKS_PATH=$mysysroot/tmp/aux/hooks
+export DIB_ARGS=\"%s\"
+export DIB_MANIFEST_SAVE_DIR=\"$mysysroot/tmp/aux/out/${IMAGE_NAME}.d\"
+export IMAGE_BLOCK_DEVICE=$blockdev
+export IMAGE_ELEMENT=\"%s\"
+export DIB_ENV=%s
+export DIB_DEBUG_TRACE=%d
+
+export TMP_BUILD_DIR=$mysysroot/tmp/aux
+export TMP_IMAGE_DIR=$mysysroot/tmp/aux
+
+if [ -n \"$mysysroot\" ]; then
+ export PATH=$mysysroot/tmp/aux/fake-bin:$PATH
+else
+ export PATH=$PATH:/usr/local/bin:/usr/local/sbin
+fi
+
+ENVIRONMENT_D_DIR=$target_dir/../environment.d
+
+if [ -d $ENVIRONMENT_D_DIR ] ; then
+ env_files=$(find $ENVIRONMENT_D_DIR -maxdepth 1 -xtype f | \
+ grep -E \"/[0-9A-Za-z_\\.-]+$\" | \
+ LANG=C sort -n)
+ for env_file in $env_files ; do
+ source $env_file
+ done
+fi
+
+if [ -n \"$new_wd\" ]; then
+ cd \"$mysysroot/$new_wd\"
+fi
+
+$target_dir/$script
+"
+ (if debug >= 1 then "set -x\n" else "")
+ (String.concat "\n" envvars_string)
+ network_string
+ out_name
+ rootfs_uuid
+ root_label
+ arch
+ dib_args
+ (String.concat " " (StringSet.elements all_elements))
+ (quote dib_vars)
+ debug in
+ write_script (destdir // "run-part.sh") script_run_part;
+ let script_run_and_log = "\
+#!/bin/bash
+logfile=$1
+shift
+exec 3>&1
+exit `( ( ( $(dirname $0)/run-part.sh \"$@\" ) 2>&1 3>&-; echo $?
>&4) | tee -a $logfile >&3 >&2) 4>&1`
+" in
+ write_script (destdir // "run-and-log.sh") script_run_and_log;
+
+ (* Create the fake sudo support. *)
+ do_mkdir (destdir // "fake-bin");
+ let fake_sudo = "\
+#!/bin/bash
+
+SCRIPTNAME=fake-sudo
+
+ARGS_SHORT=\"EHiu:\"
+ARGS_LONG=\"\"
+TEMP=`POSIXLY_CORRECT=1 getopt ${ARGS_SHORT:+-o $ARGS_SHORT} ${ARGS_LONG:+--long
$ARGS_LONG} \
+ -n \"$SCRIPTNAME\" -- \"$@\"`
+if [ $? != 0 ]; then echo \"Terminating...\" >&2 ; exit 1 ; fi
+eval set -- \"$TEMP\"
+
+preserve_env=
+set_home=
+login_shell=
+user=
+
+while true; do
+ case \"$1\" in
+ -E) preserve_env=1; shift;;
+ -H) set_home=1; shift;;
+ -i) login_shell=1; shift;;
+ -u) user=$2; shift 2;;
+ --) shift; break;;
+ *) echo \"Internal error!\"; exit 1;;
+ esac
+done
+
+if [ -n \"$user\" ]; then
+ if [ $user != root -a $user != `whoami` ]; then
+ echo \"$SCRIPTNAME: cannot use the sudo user $user, only root and $(whoami)
handled\" >&2
+ exit 1
+ fi
+fi
+
+if [ -z \"$preserve_env\" ]; then
+ for envvar in `env | cut -d= -f1`; do
+ case \"$envvar\" in
+ PATH | USER | USERNAME | HOSTNAME | TERM | LANG | HOME | SHELL | LOGNAME ) ;;
+ *) unset $envvar ;;
+ esac
+ done
+fi
+
+cmd=$1
+shift
+$cmd \"$@\"
+" in
+ write_script (destdir // "fake-bin" // "sudo") fake_sudo;
+ (* Pick dib-run-parts from the host, if available, otherwise put
+ * a fake executable which will error out if used.
+ *)
+ if check_tool ~param:"--list ." "dib-run-parts" then (
+ let lines = external_command ~prog (sprintf "which dib-run-parts") in
+ let lines = List.filter (fun x -> x <> "") lines in
+ let loc =
+ match lines with
+ | [] -> error (f_"empty output of `which dib-run-parts`")
+ | x :: _ -> x in
+ do_cp loc (destdir // "fake-bin")
+ ) else (
+ let fake_dib_run_parts = "\
+#!/bin/sh
+echo \"Please install dib-run-parts on the host\"
+exit 1
+" in
+ write_script (destdir // "fake-bin" // "dib-run-parts")
fake_dib_run_parts;
+ );
+
+ (* Write the custom hooks. *)
+ let script_install_type_env = sprintf "\
+export DIB_DEFAULT_INSTALLTYPE=\"%s\"
+"
+ install_type in
+ write_script (destdir // "hooks" // "environment.d" //
"11-dib-install-type.bash") script_install_type_env;
+
+ (* Write install-packages.sh *)
+ let script_install_packages = sprintf "\
+#!/bin/bash
+install-packages %s
+"
+ (String.concat " " extra_packages) in
+ write_script (destdir // "install-packages.sh") script_install_packages;
+
+ do_mkdir (destdir // "perm")
+
+let timing_output ~target_name entries timings =
+ let buf = Buffer.create 4096 in
+ Buffer.add_string buf "----------------------- PROFILING
-----------------------\n";
+ Buffer.add_char buf '\n';
+ bprintf buf "Target: %s\n" target_name;
+ Buffer.add_char buf '\n';
+ bprintf buf "%-40s %9s\n" "Script" "Seconds";
+ bprintf buf "%-40s %9s\n" "---------------------------------------"
"----------";
+ Buffer.add_char buf '\n';
+ List.iter (
+ fun x ->
+ bprintf buf "%-40s %10.3f\n" x (Hashtbl.find timings x);
+ ) entries;
+ Buffer.add_char buf '\n';
+ Buffer.add_string buf "--------------------- END PROFILING
---------------------\n";
+ Buffer.contents buf
+
+let is_string_valid str =
+ let is_char_valid = function
+ | 'A'..'Z' | 'a'..'z' | '0'..'9' |
'_' | '-' -> true
+ | _ -> false in
+ let rec loop str i len =
+ if i = len then
+ true
+ else if not (is_char_valid str.[i]) then
+ false
+ else
+ loop str (i + 1) len in
+ loop str 0 (String.length str)
+
+type sysroot_type =
+ | In
+ | Out
+ | Subroot
+
+let timed_run fn =
+ let time_before = Unix.gettimeofday () in
+ fn ();
+ let time_after = Unix.gettimeofday () in
+ time_after -. time_before
+
+let run_parts ~debug ~dryrun ~sysroot ~blockdev ~log_file ?(new_wd = "")
+ (g : Guestfs.guestfs) hook_dir =
+ let msg fs = make_message_function ~quiet:false fs in (* XXX *)
+ let hook_name = Filename.basename hook_dir in
+ let entries = Array.to_list (g#ls hook_dir) in
+ let entries = List.filter (fun x -> is_string_valid x) entries in
+ let entries = List.filter (
+ fun x ->
+ let fn = hook_dir ^ "/" ^ x in
+ ((g#stat fn).G.mode &^ 0o111_L > 0_L) && g#is_file
~followsymlinks:true fn
+ ) entries in
+ let entries = List.sort digit_prefix_compare entries in
+ let outbuf = Buffer.create 16384 in
+ if dryrun then (
+ List.iter (fun x -> msg (f_"Would run: %s/%s") hook_name x) entries
+ ) else (
+ let timings = Hashtbl.create 13 in
+ let new_wd =
+ match sysroot, new_wd with
+ | (Out|Subroot), "" -> "''"
+ | _, dir -> dir in
+ List.iter (
+ fun x ->
+ msg (f_"Running: %s/%s") hook_name x;
+ g#write_append log_file (sprintf "Running %s/%s...\n" hook_name x);
+ let out = ref "" in
+ let run () =
+ let outstr =
+ match sysroot with
+ | In ->
+ g#sh (sprintf "/tmp/aux/run-and-log.sh '%s' ''
'' '%s' '%s' '%s' '%s'" log_file blockdev
hook_dir new_wd x)
+ | Out ->
+ g#debug "sh" [| "/sysroot/tmp/aux/run-and-log.sh";
"/sysroot" ^ log_file; "/sysroot"; "/sysroot"; blockdev;
"/sysroot" ^ hook_dir; new_wd; x |]
+ | Subroot ->
+ g#debug "sh" [| "/sysroot/tmp/aux/run-and-log.sh";
"/sysroot" ^ log_file; "/sysroot/subroot"; "/sysroot";
blockdev; "/sysroot" ^ hook_dir; new_wd; x |] in
+ out := outstr;
+ Buffer.add_string outbuf outstr in
+ let delta_t = timed_run run in
+ Buffer.add_char outbuf '\n';
+ out := ensure_trailing_newline !out;
+ printf "%s%!" !out;
+ if debug >= 1 then (
+ printf "%s completed\n" x
+ );
+ Hashtbl.add timings x delta_t;
+ ) entries;
+ g#write_append log_file (timing_output ~target_name:hook_name entries timings)
+ );
+ flush_all ();
+ Buffer.contents outbuf
+
+let run_parts_host ~dryrun ~debug hook_dir run_script =
+ let msg fs = make_message_function ~quiet:false fs in (* XXX *)
+ let hook_name = Filename.basename hook_dir in
+ let entries = Array.to_list (Sys.readdir hook_dir) in
+ let entries = List.filter (fun x -> is_string_valid x) entries in
+ let entries = List.filter (
+ fun x ->
+ try
+ let s = Unix.stat (hook_dir // x) in
+ s.Unix.st_kind = Unix.S_REG && s.Unix.st_perm land 0o111 > 0
+ with Unix.Unix_error _ -> false
+ ) entries in
+ let entries = List.sort digit_prefix_compare entries in
+ if dryrun then (
+ List.iter (fun x -> msg (f_"Would run: %s/%s") hook_name x) entries
+ ) else (
+ let timings = Hashtbl.create 13 in
+ List.iter (
+ fun x ->
+ msg (f_"Running: %s/%s") hook_name x;
+ let cmd = sprintf "%s %s %s" (quote run_script) (quote hook_dir) (quote
x) in
+ let run () =
+ run_command ~tool:(Filename.basename run_script) cmd in
+ let delta_t = timed_run run in
+ if debug >= 1 then (
+ printf "\n";
+ printf "%s completed\n" x
+ );
+ Hashtbl.add timings x delta_t;
+
+ ) entries;
+ if debug >= 1 then (
+ print_string (timing_output ~target_name:hook_name entries timings)
+ )
+ );
+ flush_all ()
+
+let run_install_packages ~debug ~dryrun ~blockdev ~log_file
+ (g : Guestfs.guestfs) packages =
+ let msg fs = make_message_function ~quiet:false fs in (* XXX *)
+ let pkgs_string = String.concat " " packages in
+ let outbuf = Buffer.create 16384 in
+ if dryrun then (
+ msg (f_"Would install: %s") pkgs_string
+ ) else (
+ msg (f_"Installing: %s") pkgs_string;
+ g#write_append log_file (sprintf "Installing %s...\n" pkgs_string);
+ let out = g#sh (sprintf "/tmp/aux/run-and-log.sh '%s' ''
'' '%s' '/tmp/aux' '' 'install-packages.sh'"
log_file blockdev) in
+ Buffer.add_string outbuf out;
+ Buffer.add_char outbuf '\n';
+ let out = ensure_trailing_newline out in
+ printf "%s%!" out;
+ if debug >= 1 then (
+ printf "package installation completed\n";
+ );
+ );
+ flush_all ();
+ Buffer.contents outbuf
+
+let main () =
+ let verbose, trace, debug, dryrun, basepath, elements, excluded_elements,
element_paths,
+ blacklist, use_base, drive,
+ image_name, fs_type, size, root_label, install_type, image_cache, compressed,
+ qemu_img_options, is_ramdisk, ramdisk_element, extra_packages,
+ memsize, network, smp, delete_on_failure, formats, arch, envvars =
+ parse_args ~prog in
+ let msg fs = make_message_function ~quiet:false fs in (* XXX *)
+
+ (* Check that the specified base directory of diskimage-builder
+ * has the "die" script in it, so we know the directory is the
+ * right one (hopefully so, at least).
+ *)
+ if not (Sys.file_exists (basepath // "die")) then
+ error (f_"the specified base path is not the diskimage-builder library");
+
+ require_tool "uuidgen";
+
+ let image_name_d = image_name ^ ".d" in
+
+ let tmpdir = Mkdtemp.temp_dir "dib." "" in
+ rmdir_on_exit tmpdir;
+ let auxtmpdir = tmpdir // "aux" in
+ do_mkdir auxtmpdir;
+ let hookstmpdir = auxtmpdir // "hooks" in
+ do_mkdir hookstmpdir;
+ let extradatatmpdir = tmpdir // "extra-data" in
+ do_mkdir extradatatmpdir;
+ do_mkdir_p (auxtmpdir // "out" // image_name_d);
+ let elements = if use_base then ["base"] @ elements else elements in
+ let elements = if is_ramdisk then [ramdisk_element] @ elements else elements in
+ msg (f_"Elements: %s") (String.concat " " elements);
+ if debug >= 1 then (
+ printf "tmpdir: %s\n" tmpdir;
+ printf "element paths: %s\n" (String.concat ":" element_paths);
+ );
+
+ let loaded_elements = load_elements ~debug element_paths in
+ if debug >= 1 then (
+ printf "loaded elements:\n";
+ Hashtbl.iter (
+ fun k v ->
+ printf " %s => %s\n" k v.directory;
+ Hashtbl.iter (
+ fun k v ->
+ printf "\t%-20s %s\n" k (String.concat " " (List.map (fun
x -> Filename.basename x) v))
+ ) v.hooks;
+ ) loaded_elements;
+ printf "\n";
+ );
+ let all_elements = load_dependencies elements element_paths in
+ let all_elements = exclude_elements all_elements
+ (excluded_elements @ builtin_elements_blacklist) in
+
+ msg (f_"Expanded elements: %s") (String.concat " "
(StringSet.elements all_elements));
+
+ let envvars = read_envvars envvars in
+ msg (f_"Carried environment variables: %s") (String.concat " "
(List.map (fun (var, value) -> var) envvars));
+ if debug >= 1 then (
+ printf "carried over envvars:\n";
+ if envvars <> [] then
+ List.iter (
+ fun (var, value) ->
+ printf " %s=%s\n" var value
+ ) envvars
+ else
+ printf " (none)\n";
+ printf "\n";
+ );
+ let dib_args = make_dib_args Sys.argv in
+ let dib_vars = read_dib_envvars () in
+ if debug >= 1 then (
+ printf "DIB args:\n%s\n" dib_args;
+ printf "DIB envvars:\n%s\n" dib_vars
+ );
+
+ msg (f_"Preparing auxiliary data");
+
+ copy_elements all_elements loaded_elements
+ (blacklist @ builtin_scripts_blacklist) hookstmpdir;
+
+ let log_file = "/tmp/aux/perm/" ^ (log_filename ~prog) in
+
+ let arch =
+ match arch with
+ | "" -> current_arch ()
+ | arch -> arch in
+
+ let root_label =
+ match root_label with
+ | None ->
+ (* XFS has a limit of 12 characters for filesystem labels.
+ * Not changing the default for other filesystems to maintain
+ * backwards compatibility.
+ *)
+ (match fs_type with
+ | "xfs" -> "img-rootfs"
+ | _ -> "cloudimg-rootfs")
+ | Some label -> label in
+
+ let image_cache =
+ match image_cache with
+ | None -> Sys.getenv "HOME" // ".cache" //
"image-create"
+ | Some dir -> dir in
+ do_mkdir_p image_cache;
+
+ let rootfs_uuid = uuidgen ~prog () in
+
+ let formats_img, formats_archive = List.partition (
+ function
+ | "qcow2" | "raw" -> true
+ | _ -> false
+ ) formats in
+ let formats_img_nonraw = List.filter (fun x -> x <> "raw")
formats_img in
+
+ prepare_aux ~envvars ~dib_args ~dib_vars ~log_file ~out_name:image_name ~rootfs_uuid
+ ~arch ~network ~root_label ~install_type ~debug ~extra_packages
+ auxtmpdir all_elements;
+
+ let delete_output_file = ref delete_on_failure in
+ let delete_file () =
+ if !delete_output_file then (
+ List.iter (
+ fun fmt ->
+ try unlink (output_filename image_name fmt) with _ -> ()
+ ) formats
+ )
+ in
+ at_exit delete_file;
+
+ prepare_external ~dib_args ~dib_vars ~out_name:image_name ~root_label ~rootfs_uuid
+ ~image_cache ~arch ~network ~debug
+ tmpdir basepath hookstmpdir extradatatmpdir (auxtmpdir // "fake-bin")
+ all_elements element_paths;
+
+ let run_hook_host hook =
+ let hook_dir = hookstmpdir // (hook ^ ".d") in
+ if is_directory hook_dir then (
+ if debug >= 1 then (
+ printf "Running hooks for %s...\n%!" hook;
+ printf "run-parts %s\n%!" (quote hook_dir)
+ );
+ run_parts_host ~dryrun ~debug hook_dir (tmpdir // "run-part-extra.sh")
+ )
+ and run_hook ~blockdev ~sysroot ?(new_wd = "")
+ (g : Guestfs.guestfs) hook =
+ let hook_dir = sprintf "/tmp/aux/hooks/%s.d" hook in
+ if g#is_dir hook_dir then (
+ if debug >= 1 then (
+ printf "Running hooks for %s...\n%!" hook;
+ printf "run-parts %s\n%!" (quote hook_dir)
+ );
+ run_parts ~debug ~dryrun ~sysroot ~blockdev ~log_file ~new_wd g hook_dir
+ ) else (
+ ""
+ ) in
+
+ run_hook_host "extra-data";
+
+ let copy_in (g : Guestfs.guestfs) srcdir destdir =
+ let desttar = Filename.temp_file ~temp_dir:tmpdir "virt-dib."
".tar.gz" in
+ let cmd = sprintf "tar czf %s -C %s --owner=root --group=root ."
+ (quote desttar) (quote srcdir) in
+ run_command cmd;
+ g#mkdir_p destdir;
+ g#tar_in ~compress:"gzip" desttar destdir;
+ Sys.remove desttar in
+
+ let copy_preserve_in (g : Guestfs.guestfs) srcdir destdir =
+ let desttar = Filename.temp_file ~temp_dir:tmpdir "virt-dib."
".tar.gz" in
+ let remotetar = "/tmp/aux/" ^ (Filename.basename desttar) in
+ let cmd = sprintf "tar czf %s -C %s --owner=root --group=root ."
+ (quote desttar) (quote srcdir) in
+ run_command cmd;
+ g#upload desttar remotetar;
+ let verbose_flag = if debug > 0 then "v" else "" in
+ ignore (g#debug "sh" [| "tar"; "-C";
"/sysroot" ^ destdir; "--no-overwrite-dir"; "-x" ^
verbose_flag ^ "zf"; "/sysroot" ^ remotetar |]);
+ Sys.remove desttar;
+ g#rm remotetar in
+
+ if debug >= 1 then
+ ignore (Sys.command (sprintf "tree -ps %s" (quote tmpdir)));
+
+ msg (f_"Opening the disks");
+
+ let g, tmpdisk, tmpdiskfmt, drive_partition =
+ let g = new G.guestfs () in
+ if verbose then g#set_verbose true;
+ if trace then g#set_trace true;
+
+ (match memsize with None -> () | Some memsize -> g#set_memsize memsize);
+ (match smp with None -> () | Some smp -> g#set_smp smp);
+ g#set_network network;
+
+ (* Make sure to turn SELinux off to avoid awkward interactions
+ * between the appliance kernel and applications/libraries interacting
+ * with SELinux xattrs.
+ *)
+ g#set_selinux false;
+
+ (* Main disk with the built image. *)
+ let fmt = "raw" in
+ let fn =
+ (* If "raw" is among the selected outputs, use it as main backing
+ * disk, otherwise create a temporary disk.
+ *)
+ if not is_ramdisk && not (StringSet.mem "ironic-agent"
all_elements) && List.mem "raw" formats_img then image_name
+ else Filename.temp_file ~temp_dir:tmpdir "image." "" in
+ let fn = output_filename fn fmt in
+ (* Produce the output image. *)
+ g#disk_create fn fmt size;
+ g#add_drive ~readonly:false ~format:fmt fn;
+
+ (* Helper drive for elements and binaries. *)
+ g#add_drive_scratch (unit_GB 5);
+
+ (match drive with
+ | None ->
+ g#add_drive_scratch (unit_GB 5)
+ | Some drive ->
+ g#add_drive drive;
+ );
+
+ g#launch ();
+
+ (* Prepare the /aux partition. *)
+ g#mkfs "ext2" "/dev/sdb";
+ g#mount "/dev/sdb" "/";
+
+ copy_in g auxtmpdir "/";
+ copy_in g basepath "/lib";
+ g#umount "/";
+
+ (* Prepare the /aux/perm partition. *)
+ let drive_partition =
+ match drive with
+ | None ->
+ g#mkfs "ext2" "/dev/sdc";
+ "/dev/sdc"
+ | Some _ ->
+ let partitions = Array.to_list (g#list_partitions ()) in
+ (match partitions with
+ | [] -> "/dev/sdc"
+ | partitions ->
+ let partitions = List.filter (
+ fun x ->
+ string_prefix x "/dev/sdc"
+ ) partitions in
+ if partitions = [] then
+ error (f_"no partitions found in the helper drive");
+ List.hd partitions
+ ) in
+ g#mount drive_partition "/";
+ g#mkdir_p "/home/.cache/image-create";
+ g#umount "/";
+
+ g, fn, fmt, drive_partition in
+
+ let mount_aux () =
+ g#mkmountpoint "/tmp/aux";
+ g#mount "/dev/sdb" "/tmp/aux";
+ g#mount drive_partition "/tmp/aux/perm" in
+
+ (* Small kludge: try to umount all first: if that fails, use lsof and fuser
+ * to find out what might have caused the failure, run udevadm to try
+ * to settle things down (udev, you never know), and try umount all again
+ *)
+ let checked_umount_all () =
+ try g#umount_all ()
+ with G.Error _ ->
+ if debug >= 1 then (
+ (try printf "lsof:\n%s\nEND\n" (g#debug "sh" [|
"lsof"; "/sysroot"; |]) with _ -> ());
+ (try printf "fuser:\n%s\nEND\n" (g#debug "sh" [|
"fuser"; "-v"; "-m"; "/sysroot"; |]) with _ ->
());
+ (try printf "losetup:\n%s\nEND\n" (g#debug "sh" [|
"losetup"; "--list"; "--all" |]) with _ -> ());
+ );
+ ignore (g#debug "sh" [| "udevadm"; "--debug";
"settle" |]);
+ g#umount_all ()
+ in
+
+ g#mkmountpoint "/tmp";
+ mount_aux ();
+
+ let blockdev =
+ (* Setup a loopback device, just like d-i-b would tie an image in the host
+ * environment.
+ *)
+ let run_losetup device =
+ let lines = g#debug "sh" [| "losetup"; "--show";
"-f"; device |] in
+ let lines = string_nsplit "\n" lines in
+ let lines = List.filter (fun x -> x <> "") lines in
+ (match lines with
+ | [] -> device
+ | x :: _ -> x
+ ) in
+ let blockdev = run_losetup "/dev/sda" in
+
+ let run_hook_out_eval hook envvar =
+ let lines = run_hook ~sysroot:Out ~blockdev g hook in
+ let lines = string_nsplit "\n" lines in
+ let lines = List.filter (fun x -> x <> "") lines in
+ if lines = [] then None
+ else (try Some (var_from_lines envvar lines) with _ -> None) in
+
+ (match run_hook_out_eval "block-device" "IMAGE_BLOCK_DEVICE"
with
+ | None -> blockdev
+ | Some x -> x
+ ) in
+
+ let rec run_hook_out ?(new_wd = "") hook =
+ do_run_hooks_noout ~sysroot:Out ~new_wd hook
+ and run_hook_in hook =
+ do_run_hooks_noout ~sysroot:In hook
+ and run_hook_subroot hook =
+ do_run_hooks_noout ~sysroot:Subroot hook
+ and do_run_hooks_noout ~sysroot ?(new_wd = "") hook =
+ ignore (run_hook ~sysroot ~blockdev ~new_wd g hook) in
+
+ g#sync ();
+ checked_umount_all ();
+ flush_all ();
+
+ msg (f_"Setting up the destination root");
+
+ (* Create and mount the target filesystem. *)
+ let mkfs_options = [] in
+ let mkfs_options =
+ mkfs_options @
+ (match fs_type with
+ | "ext4" ->
+ (* Very conservative to handle images being resized a lot
+ * Without -J option specified, default journal size will be set to 32M
+ * and online resize will be failed with error of needs too many credits.
+ *)
+ [ "-i"; "4096"; "-J"; "size=64" ]
+ | _ -> []
+ ) in
+ let mkfs_options = mkfs_options @ [ "-t"; fs_type; blockdev ] in
+ ignore (g#debug "sh" (Array.of_list ([ "mkfs" ] @ mkfs_options)));
+ g#set_label blockdev root_label;
+ (match fs_type with
+ | x when string_prefix x "ext" -> g#set_uuid blockdev rootfs_uuid
+ | _ -> ());
+ g#mount blockdev "/";
+ g#mkmountpoint "/tmp";
+ mount_aux ();
+ g#mkdir "/subroot";
+
+ run_hook_subroot "root";
+
+ g#sync ();
+ checked_umount_all ();
+ flush_all ();
+
+ g#mount blockdev "/";
+ g#rm_rf "/tmp";
+ let subroot_items =
+ let l = Array.to_list (g#ls "/subroot") in
+ let l_lost_plus_found, l = List.partition (fun x -> x = "lost+found") l
in
+ if l_lost_plus_found <> [] then (
+ g#rm_rf "/subroot/lost+found";
+ );
+ l in
+ List.iter (fun x -> g#mv ("/subroot/" ^ x) ("/" ^ x))
subroot_items;
+ g#rmdir "/subroot";
+ (* Check /tmp exists already. *)
+ ignore (g#is_dir "/tmp");
+ mount_aux ();
+ g#ln_s "aux/hooks" "/tmp/in_target.d";
+
+ copy_preserve_in g extradatatmpdir "/";
+
+ run_hook_in "pre-install";
+
+ if extra_packages <> [] then
+ ignore (run_install_packages ~debug ~dryrun ~blockdev ~log_file g extra_packages);
+
+ run_hook_in "install";
+
+ run_hook_in "post-install";
+
+ run_hook_in "finalise";
+
+ run_hook_out ~new_wd:("/tmp/aux/out/" ^ image_name_d) "cleanup";
+
+ g#sync ();
+
+ if g#ls "/tmp/aux/out/" <> [||] then (
+ msg (f_"Extracting data out of the image");
+ do_mkdir image_name_d;
+ g#copy_out ("/tmp/aux/out/" ^ image_name_d) ".";
+ );
+
+ g#rm "/tmp/in_target.d";
+ g#umount "/tmp/aux/perm";
+ g#umount "/tmp/aux";
+ g#rmdir "/tmp/aux";
+
+ flush_all ();
+
+ List.iter (
+ fun fmt ->
+ let fn = output_filename image_name fmt in
+ match fmt with
+ | "tar" ->
+ msg (f_"Compressing the image as tar");
+ g#tar_out ~excludes:[| "./sys/*"; "./proc/*";
"./tmp/*"; |]
+ "/" fn
+ | _ as fmt -> error "unhandled format: %s" fmt
+ ) formats_archive;
+
+ msg (f_"Umounting the disks");
+
+ (* Now that we've finished the build, don't delete the output file on
+ * exit.
+ *)
+ delete_output_file := false;
+
+ g#sync ();
+ checked_umount_all ();
+ g#shutdown ();
+ g#close ();
+
+ flush_all ();
+
+ if not is_ramdisk && not (StringSet.mem "ironic-agent" all_elements)
&& formats_img_nonraw <> [] then (
+ let can_compress = function
+ | "qcow2" -> true
+ | _ -> false in
+ let tmpdisk = quote tmpdisk in
+ List.iter (
+ fun fmt ->
+ let fn = output_filename image_name fmt in
+ msg (f_"Converting to %s") fmt;
+ match fmt with
+ | "qcow2" ->
+ let cmd =
+ sprintf "qemu-img convert%s -f %s %s -O %s%s %s"
+ (if compressed && can_compress fmt then " -c" else
"")
+ tmpdiskfmt
+ tmpdisk
+ fmt
+ (match qemu_img_options with
+ | None -> ""
+ | Some opt -> " -o " ^ quote opt)
+ (quote fn) in
+ if debug >= 1 then
+ printf "%s\n%!" cmd;
+ run_command cmd
+ | _ as fmt -> error "unhandled format: %s" fmt
+ ) formats_img_nonraw;
+ );
+
+ msg (f_"Done")
+
+let () = run_main_and_handle_errors ~prog main
diff --git a/dib/elements.ml b/dib/elements.ml
new file mode 100644
index 0000000..cae6c3c
--- /dev/null
+++ b/dib/elements.ml
@@ -0,0 +1,219 @@
+(* virt-dib
+ * Copyright (C) 2015 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.
+ *)
+
+(* Parsing and handling of elements. *)
+
+open Common_gettext.Gettext
+open Common_utils
+
+open Utils
+
+open Printf
+
+module StringSet = Set.Make (String)
+
+type element = {
+ directory : string;
+ hooks : hooks_map;
+}
+and hooks_map = (string, string list) Hashtbl.t
+
+exception Duplicate_script of string * string (* hook, script *)
+
+(* These are the elements which we don't ever try to use.
+ *)
+let builtin_elements_blacklist = [
+]
+
+(* These are the scripts which we don't ever try to run.
+ * Usual reason could be that they are not compatible the way virt-dib works
+ * (e.g. they expect the tree of elements outside the chroot, which is not
+ * available in the appliance. *)
+let builtin_scripts_blacklist = [
+ "01-sahara-version"; (* Gets the Git commit ID of the d-i-b and
+ * sahara-image-elements repositories. *)
+]
+
+let stringset_of_list l =
+ List.fold_left (fun acc x -> StringSet.add x acc) StringSet.empty l
+
+let quote = Filename.quote
+
+let load_hooks ~debug path =
+(*
+ if debug >= 2 then
+ printf "load_hooks %s\n" path;
+*)
+ let hooks = Hashtbl.create 13 in
+ let entries = Sys.readdir path in
+ Array.iter (
+ fun e ->
+(*
+ if debug >= 2 then
+ printf "moo! %s\n" path;
+*)
+ let p = path // e in
+ if is_directory p && (Filename.check_suffix e ".d") then (
+ let listing = Sys.readdir p in
+ let hook = String.sub e 0 ((String.length e) - 2) in
+ Array.iter (
+ fun script ->
+(*
+ if debug >= 2 then
+ printf "%s, %s\n" path p;
+*)
+ try
+ (match script.[0] with
+ | '0'..'9' ->
+ let d = p // script in
+ let l = try Hashtbl.find hooks hook with Not_found -> [] in
+ let l = d :: l in
+ Hashtbl.replace hooks hook l
+ | _ -> ()
+ )
+ with Invalid_argument _ -> ()
+ ) listing
+ )
+ ) entries;
+ hooks
+
+let load_elements ~debug paths =
+ let loaded_elements = Hashtbl.create 13 in
+ List.iter (
+ fun path ->
+(* printf "moo! %s\n" path; *)
+ if Sys.is_directory path then (
+ let listing = Sys.readdir path in
+ Array.iter (
+ fun p ->
+(* printf "%s, %s\n" path p; *)
+ let dir = path // p in
+ if is_directory dir then (
+ if not (Hashtbl.mem loaded_elements p) then (
+ let elem = { directory = dir; hooks = load_hooks ~debug dir } in
+ Hashtbl.add loaded_elements p elem
+ ) else if debug >= 1 then (
+ printf "element %s (in %s) already present" p path;
+ )
+ )
+ ) listing
+ )
+ ) paths;
+ loaded_elements
+
+let load_dependencies elements element_paths =
+ let get filename element =
+ let rec loop = function
+ | [] ->
+ error (f_"element %s not found in %s") element (String.concat
":" element_paths)
+ | x :: tl ->
+(* printf "@@@ element %s, path %s\n" element x; *)
+ if is_directory (x // element) then (
+ let path = x // element // filename in
+ if Sys.file_exists path then (
+ let lines = read_whole_file path in
+ let lines = string_nsplit "\n" lines in
+ let lines = List.filter (fun x -> x <> "") lines in
+ stringset_of_list lines
+ ) else
+ StringSet.empty
+ ) else
+ loop tl in
+ loop element_paths in
+ let get_deps = get "element-deps" in
+ let get_provides = get "element-provides" in
+
+ let queue = Queue.create () in
+ let final = ref StringSet.empty in
+ let provided = ref StringSet.empty in
+ List.iter (fun x -> Queue.push x queue) elements;
+ final := stringset_of_list elements;
+ while not (Queue.is_empty queue) do
+ let elem = Queue.pop queue in
+(* printf "@@ %s\n" elem; *)
+ if StringSet.mem elem !provided <> true then (
+ let deps = get_deps elem in
+(* printf ">> %s, deps: %s\n" elem (String.concat ","
(StringSet.elements deps)); *)
+(* printf ">> %s, prov: %s\n" elem (String.concat ","
(StringSet.elements (get "element-provides" elem))); *)
+ provided := StringSet.union !provided (get_provides elem);
+ StringSet.iter (fun x -> Queue.push x queue)
+ (StringSet.diff deps (StringSet.union !final !provided));
+ final := StringSet.union !final deps
+ )
+ done;
+ let conflicts = StringSet.inter (stringset_of_list elements) !provided in
+ if not (StringSet.is_empty conflicts) then
+ error (f_"following elements were explicitly required but are provided by other
included elements: %s")
+ (String.concat "," (StringSet.elements conflicts));
+ if not (StringSet.mem "operating-system" !provided) then
+ error (f_"please include an operating system element");
+ StringSet.diff !final !provided
+
+let elements_with_script elements loaded_elements hook scriptname =
+ StringSet.filter (
+ fun e ->
+ try
+ let s = Hashtbl.find (Hashtbl.find loaded_elements e).hooks hook in
+ List.exists (
+ fun p ->
+ Filename.basename p = scriptname
+ ) s
+ with Not_found -> false
+ ) elements
+
+let copy_element element destdir blacklist =
+ let entries = Array.to_list (Sys.readdir element.directory) in
+ let entries = List.filter (
+ fun e -> e <> "tests"
+ ) entries in
+ List.iter (
+ fun e ->
+ let path = element.directory // e in
+ let destpath = destdir // e in
+ if is_directory path then (
+ do_mkdir destpath;
+ let subentries = Sys.readdir path in
+ Array.iter (
+ fun sube ->
+ if is_file_or_link (destpath // sube) then (
+ let hook =
+ if Filename.check_suffix e ".d" then
+ String.sub e 0 ((String.length e) - 2)
+ else
+ e in
+ raise (Duplicate_script (hook, sube))
+ ) else if not (List.mem sube blacklist) then
+ do_cp (path // sube) destpath
+ ) subentries;
+ ) else (
+ do_cp path destdir
+ )
+ ) entries
+
+let copy_elements elements loaded_elements blacklist destdir =
+ do_mkdir destdir;
+ StringSet.iter (
+ fun element ->
+ try
+ copy_element (Hashtbl.find loaded_elements element) destdir blacklist
+ with
+ | Duplicate_script (hook, script) ->
+ let dups = elements_with_script elements loaded_elements hook script in
+ error (f_"There is a duplicated hook in your elements:\n%s.d/%s in:
%s")
+ hook script (String.concat " " (StringSet.elements dups))
+ ) elements
diff --git a/dib/link.sh.in b/dib/link.sh.in
new file mode 100644
index 0000000..71e65ee
--- /dev/null
+++ b/dib/link.sh.in
@@ -0,0 +1,22 @@
+# libguestfs Makefile.am
+# @configure_input@
+# (C) Copyright 2015 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Hack automake to link binary properly. There is no other way to add
+# the -cclib parameter to the end of the command line.
+
+exec "$@" -linkpkg -cclib '-pthread -lpthread -lutils @LIBINTL@ -lgnu'
diff --git a/dib/uname-c.c b/dib/uname-c.c
new file mode 100644
index 0000000..fc63233
--- /dev/null
+++ b/dib/uname-c.c
@@ -0,0 +1,57 @@
+/* virt-builder
+ * Copyright (C) 2014 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <sys/utsname.h>
+
+#include <caml/alloc.h>
+#include <caml/fail.h>
+#include <caml/memory.h>
+#include <caml/mlvalues.h>
+
+#ifdef HAVE_CAML_UNIXSUPPORT_H
+#include <caml/unixsupport.h>
+#else
+#define Nothing ((value) 0)
+extern void unix_error (int errcode, char * cmdname, value arg) Noreturn;
+#endif
+
+extern value virt_builder_uname (value unit);
+
+value
+virt_builder_uname (value unit)
+{
+ CAMLparam0 ();
+ CAMLlocal1 (rv);
+ struct utsname u;
+
+ if (uname (&u) < 0)
+ unix_error (errno, (char *) "uname", Val_int (0));
+
+ rv = caml_alloc (5, 0);
+
+ Store_field (rv, 0, caml_copy_string (u.sysname));
+ Store_field (rv, 1, caml_copy_string (u.nodename));
+ Store_field (rv, 2, caml_copy_string (u.release));
+ Store_field (rv, 3, caml_copy_string (u.version));
+ Store_field (rv, 4, caml_copy_string (u.machine));
+
+ CAMLreturn (rv);
+}
diff --git a/dib/uname.ml b/dib/uname.ml
new file mode 100644
index 0000000..c370c2c
--- /dev/null
+++ b/dib/uname.ml
@@ -0,0 +1,27 @@
+(* virt-builder
+ * Copyright (C) 2014 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+type uname_struct = {
+ sysname : string;
+ nodename : string;
+ release : string;
+ version : string;
+ machine : string;
+}
+
+external uname : unit -> uname_struct = "virt_builder_uname"
diff --git a/dib/uname.mli b/dib/uname.mli
new file mode 100644
index 0000000..aea441b
--- /dev/null
+++ b/dib/uname.mli
@@ -0,0 +1,28 @@
+(* virt-builder
+ * Copyright (C) 2014 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+type uname_struct = {
+ sysname : string;
+ nodename : string;
+ release : string;
+ version : string;
+ machine : string;
+}
+
+val uname : unit -> uname_struct
+(** [uname] Tiny wrapper to the C [uname]. *)
diff --git a/dib/utils.ml b/dib/utils.ml
new file mode 100644
index 0000000..b18b4fe
--- /dev/null
+++ b/dib/utils.ml
@@ -0,0 +1,148 @@
+(* virt-dib
+ * Copyright (C) 2015 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 prog = Filename.basename Sys.executable_name
+let error ?exit_code fs = error ~prog ?exit_code fs
+let warning fs = warning ~prog fs
+let info fs = info ~prog fs
+
+let unit_GB howmany =
+ (Int64.of_int howmany) *^ 1024_L *^ 1024_L *^ 1024_L
+
+let current_arch () =
+ let normalize_arch = function
+ | "amd64" | "x86_64" | "x64" -> "amd64"
+ | "i386"| "i486"| "i586"| "i686" ->
"i386"
+ | arch when string_prefix arch "armv" -> "armhf"
+ | arch -> arch in
+
+ try normalize_arch ((Uname.uname ()).Uname.machine)
+ with Unix.Unix_error _ -> "unknown"
+
+let output_filename image_name = function
+ | "tgz" -> image_name ^ ".tar.gz"
+ | fmt -> image_name ^ "." ^ fmt
+
+let log_filename ~prog =
+ let tm = Unix.gmtime (Unix.time ()) in
+ sprintf "%s-%d%02d%02d-%02d%02d%02d.log"
+ prog (tm.Unix.tm_year + 1900) (tm.Unix.tm_mon + 1) tm.Unix.tm_mday
+ tm.Unix.tm_hour tm.Unix.tm_min tm.Unix.tm_sec
+
+let var_from_lines var lines =
+ let var_with_equal = var ^ "=" in
+ let lines = List.filter (fun x -> string_prefix x var_with_equal) lines in
+ let values = List.map (
+ fun x ->
+ let name, value = string_split "=" x in
+ value
+ ) lines in
+ (match values with
+ | [] ->
+ error (f_"variable '%s' not found in lines:\n%s")
+ var (String.concat "\n" lines)
+ | [x] -> x
+ | _ ->
+ error (f_"variable '%s' has more than one occurrency in
lines:\n%s")
+ var (String.concat "\n" lines)
+ )
+
+let digit_prefix_compare a b =
+ let myint str =
+ try int_of_string str
+ with _ -> 0 in
+ let mylength str =
+ match String.length str with
+ | 0 -> max_int
+ | x -> x in
+ let count_leading_digits str =
+ let rec loop str i len =
+ if i = len then
+ i
+ else match str.[i] with
+ | '0'..'9' -> loop str (i + 1) len
+ | _ -> i in
+ loop str 0 (String.length str) in
+ let split_prefix str =
+ let len = String.length str in
+ let digits = count_leading_digits str in
+ match digits with
+ | 0 -> "", str
+ | x when x = len -> str, ""
+ | _ -> String.sub str 0 digits, String.sub str digits (len - digits) in
+
+ let pref_a, rest_a = split_prefix a in
+ let pref_b, rest_b = split_prefix b in
+ match mylength pref_a, mylength pref_b, compare (myint pref_a) (myint pref_b) with
+ | x, y, 0 when x = y -> compare rest_a rest_b
+ | x, y, 0 -> x - y
+ | _, _, x -> x
+
+let do_mkdir dir =
+ mkdir_p dir 0o755
+
+let do_mkdir_p dir =
+ mkdir_p dir 0o755
+
+let rec remove_dups = function
+ | [] -> []
+ | x :: xs ->
+ x :: (remove_dups (List.filter (fun x -> x <> x) xs))
+
+let run_command ?(tool = "") cmd =
+ let res = Sys.command cmd in
+ if res <> 0 then (
+ (* Extract the executable name, if not provided. *)
+ let tool =
+ match tool with
+ | "" ->
+ let tool, _ = string_split " " cmd in
+ tool
+ | tool -> tool
+ in
+ error (f_"%s command (%s) failed with %d") tool cmd res
+ )
+
+let check_tool ?(param = "--version") tool =
+ let cmd = sprintf "%s %s 2>&1 >/dev/null" tool param in
+ let res = Sys.command cmd in
+ res = 0
+
+let require_tool ?(param = "--version") tool =
+ if not (check_tool ~param tool) then
+ error (f_"%s needed but not found (or '%s %s' could not be run)")
tool tool param
+
+let is_file_or_link file =
+ try
+ match (Unix.stat file).Unix.st_kind with
+ | Unix.S_REG | Unix.S_LNK -> true
+ | _ -> false
+ with Unix.Unix_error _ -> false
+
+let do_cp src dest =
+ let cmd = sprintf "cp -t %s -a %s" (Filename.quote dest) (Filename.quote src)
in
+ run_command cmd
+
+let ensure_trailing_newline str =
+ if String.length str > 0 && str.[String.length str - 1] <>
'\n' then str ^ "\n"
+ else str
diff --git a/dib/virt-dib.pod b/dib/virt-dib.pod
new file mode 100644
index 0000000..60b821c
--- /dev/null
+++ b/dib/virt-dib.pod
@@ -0,0 +1,541 @@
+=head1 NAME
+
+virt-dib - Run diskimage-builder elements
+
+=head1 SYNOPSIS
+
+ virt-dib -B DIB-LIB [options] elements...
+
+=head1 DESCRIPTION
+
+Virt-dib is a tool for using the elements of C<diskimage-builder>
+to build a new disk image, generate new ramdisks, etc.
+
+Virt-dib is intended as safe replacement for C<diskimage-builder>
+and its C<ramdisk-image-create> mode, see
+L</COMPARISON WITH DISKIMAGE-BUILDER> for a quick comparison with
+usage of C<diskimage-builder>.
+
+C<diskimage-builder> is part of the TripleO OpenStack project:
+L<https://wiki.openstack.org/wiki/TripleO>.
+
+=head1 EXAMPLES
+
+=head2 Build simple images of distributions
+
+ virt-dib \
+ -B /path/to/diskimage-builder/lib \
+ -p /path/to/diskimage-builder/elements \
+ --envvar DIB_RELEASE=wheezy \
+ --name debian-wheezy \
+ debian vm
+
+This builds a Debian Wheezy disk image, suitable for running
+as virtual machine, saved as F<debian-wheezy.qcow2>.
+
+=head2 Build ramdisks
+
+ virt-dib \
+ -B /path/to/diskimage-builder/lib \
+ -p /path/to/diskimage-builder/elements \
+ --ramdisk \
+ --name ramdisk \
+ ubuntu deploy-ironic
+
+This builds a ramdisk for the Ironic OpenStack component based
+on the Ubuntu distribution.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--help>
+
+Display help.
+
+=item B<--arch> ARCHITECURE
+
+Use the specified architecture for the output image. The default
+value is the same as the host running virt-dib.
+
+Notes: right now it does nothing more than setting the C<ARCH>
+environment variable for the elements.
+
+=item B<--blacklist> SCRIPT
+
+Blacklist the use of any element script named C<SCRIPT>.
+
+This can be useful in case some script does not run well with virt-dib,
+for example when they really need C<diskimage-builder>'s environment.
+
+=item B<-B> PATH
+
+Set the path to the library directory of C<diskimage-builder>. This is
+usually the C<lib> subdirectory.
+
+This parameter is B<mandatory>, as virt-dib needs to provide it for the
+elements (as some of them might use scripts in it). Virt-dib itself
+does not make use of the library directory.
+
+=item B<--debug> LEVEL
+
+Set the debug level to C<LEVEL>, which is a non-negative integer
+number. The default is C<0>.
+
+This debug level is different than what C<-x> and C<-v> set,
+and it increases the debugging information printed out.
+Specifically, this sets the C<DIB_DEBUG_TRACE>, and any value
+E<gt> C<0> enables tracing in the scripts executed.
+
+=item B<--drive> DISK
+
+Add the specified disk to be used as helper drive where to cache
+files of the elements, like disk images, distribution packages, etc.
+
+See L</HELPER DRIVE>.
+
+=item B<-p> PATH
+
+=item B<--element-path> PATH
+
+Add a new path with elements. Paths are used in the same order as the
+C<-p> parameters appear, so a path specified first is looked first,
+and so on.
+
+Obviously, it is recommended to add the path to the own elements of
+C<diskimage-builder>, as most of the other elements will rely on them.
+
+=item B<--extra-packages> PACKAGE,...
+
+Install additional packages in the image being built.
+
+This relies on the C<install-packages> binary provided by the
+package management elements.
+
+This option can be specified multiple times, each time with multiple
+packages separated by comma.
+
+=item B<--envvar> VARIABLE
+
+=item B<--envvar> VARIABLE=VALUE
+
+Carry or set an environment variable for the elements.
+
+See L</ENVIRONMENT VARIABLES> below for more information on the
+interaction and usage of environment variables.
+
+This option can be used in two ways:
+
+=over 4
+
+=item B<--envvar> VARIABLE
+
+Carry the environment variable C<VARIABLE>. If it is not set, nothing
+is exported to the elements.
+
+=item B<--envvar> VARIABLE=VALUE
+
+Set the environment variable C<VARIABLE> with value C<VALUE> for the
+elements, regardless whether an environment variable with the same
+name exists.
+
+This can be useful to pass environment variable without exporting
+them in the environment where virt-dib runs.
+
+=back
+
+=item B<--exclude-element> ELEMENT
+
+Ignore the specified element.
+
+=item B<--formats> FORMAT,...
+
+Set the list of output formats, separating them with comma.
+
+Supported formats are:
+
+=over 4
+
+=item C<qcow2> (enabled by default)
+
+QEMU's qcow2.
+
+=item C<raw>
+
+Raw disk format.
+
+=item C<tar>
+
+An uncompressed tarball.
+
+=back
+
+=item B<--fs-type> FILESYSTEM
+
+Set the filesystem type to use for the root filesystem. The default
+is C<ext4>.
+
+See also L<guestfs(3)/guestfs_filesystem_available>.
+
+=item B<--machine-readable>
+
+This option is used to make the output more machine friendly
+when being parsed by other programs. See
+L</MACHINE READABLE OUTPUT> below.
+
+=item B<-m> MB
+
+=item B<--memsize> MB
+
+Change the amount of memory allocated to the appliance. Increase
+this if you find that the virt-dib execution runs out of memory.
+
+The default can be found with this command:
+
+ guestfish get-memsize
+
+=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.
+
+The network only allows outgoing connections and has other minor
+limitations. See L<virt-rescue(1)/NETWORK>.
+
+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-dib.
+
+If you use I<--no-network>, then the environment variable
+C<DIB_OFFLINE> is set to C<1>, signaling the elements that they
+should use only cached resources when available. Note also that,
+unlike with C<diskimage-builder> where elements may still be able
+to access to the network even with C<DIB_OFFLINE=>, under virt-dib
+network will be fully unaccessible.
+
+=item B<--image-cache> DIRECTORY
+
+Set the path in the host where cache the resources used by the elements
+of the C<extra-data> phase. The default is F<~/.cache/image-create>.
+
+Please note that most of the resources fetched after C<extra-data>
+will be cached in the helper drive specified with I<--drive>;
+see also L</HELPER DRIVE>.
+
+=item B<--install-type> TYPE
+
+Specify the default installation type. Defaults to C<source>.
+
+Set to C<package> to use package based installations by default.
+
+=item B<-n>
+
+Skip the inclusion of the C<base> element.
+
+=item B<--name> NAME
+
+Set the name of the output image file. The default is C<image>.
+
+According to the chosen name, there will be the following in the
+current directory:
+
+=over 4
+
+=item F<$NAME.ext>
+
+For each output format, a disk image named after the outout image
+with the extension depending on the format; for example:
+F<$NAME.qcow2>, F<$NAME.raw>, F<$NAME.tar.gz>.
+
+Not applicable in ramdisk mode, see L</RAMDISK BUILDING>.
+
+=item F<$NAME.d>
+
+A directory containing any files created by the elements, for example
+F<dib-manifests> directory (created by the C<manifests> element),
+ramdisks and kernels in ramdisk mode, and so on.
+
+=back
+
+=item B<--no-delete-on-failure>
+
+Don't delete the output files on failure to build. You can use this to
+debug failures to run scripts.
+
+The default is to delete the output file if virt-dib fails (or,
+for example, some script that it runs fails).
+
+=item B<--qemu-img-options> option[,option,...]
+
+Pass I<--qemu-img-options> option(s) to the L<qemu-img(1)> command
+to fine-tune the output format. Options available depend on
+the output format (see I<--formats>) and the installed version
+of the qemu-img program.
+
+You should use I<--qemu-img-options> at most once. To pass multiple
+options, separate them with commas, eg:
+
+ virt-dib ... --qemu-img-options cluster_size=512,preallocation=metadata ...
+
+=item B<--ramdisk>
+
+Set the ramdisk building mode.
+
+See L</RAMDISK BUILDING>.
+
+=item B<--ramdisk-element> NAME
+
+Set the name for the additional element added in ramdisk building mode.
+The default is C<ramdisk>.
+
+See L</RAMDISK BUILDING>.
+
+=item B<--root-label> LABEL
+
+Set the label for the root filesystem in the created image.
+
+Please note that some filesystems have different restrictions on
+the length of their labels; for example, on C<ext2/3/4> filesystems
+labels cannot be longer than 16 characters, while on C<xfs> they have
+at most 12 characters.
+
+The default depends on the actual filesystem for the root partition
+(see I<--fs-type>): on C<xfs> is C<img-rootfs>, while
+C<cloudimg-rootfs> on any other filesystem.
+
+=item B<--size> SIZE
+
+Select the size of the output disk, where the size can be specified
+using common names such as C<32G> (32 gigabytes) etc.
+The default size is C<5G>.
+
+To specify size in bytes, the number must be followed by the lowercase
+letter I<b>, eg: S<C<--size 10737418240b>>.
+
+See also L<virt-resize(1)> for resizing partitions of an existing
+disk image.
+
+=item B<--smp> N
+
+Enable N E<ge> 2 virtual CPUs for scripts to use.
+
+=item B<-u>
+
+Do not compress resulting qcow2 images. The default is
+to compressed them.
+
+=item B<-v>
+
+=item B<--verbose>
+
+Enable debugging messages.
+
+=item B<-V>
+
+=item B<--version>
+
+Display version number and exit.
+
+=item B<-x>
+
+Enable tracing of libguestfs API calls.
+
+=back
+
+=head1 ENVIRONMENT VARIABLES
+
+Unlike with C<diskimage-builder>, the environment of the host is
+B<not> inherited in the appliance when running most of the elements
+(i.e. all the ones different than C<extra-data>).
+
+To set environment for the elements being run, it is necessary to tell
+virt-dib to use them, with the option I<--envvar>. Such option
+allows to selectively export environment variables when running the
+elements, and it is the preferred way to pass environment variables
+to the elements.
+
+To recap: if you want the environment variable C<MYVAR>
+(and its content) to be available to the elements, you can do either
+
+ export MYVAR # whichever is its value
+ virt-dib ... --envvar MYVAR ...
+
+or
+
+ virt-dib ... --envvar MYVAR=value_of_it ...
+
+=head1 HELPER DRIVE
+
+Virt-dib runs most of the element in its own appliance, and thus not
+on the host. Because of this, there is no possibility for elements
+to cache resources directly on the host.
+
+To solve this issue, virt-dib allows the usage of an helper drive
+where to store cached resources, like disk images,
+distribution packages, etc. While this means that there is a smaller
+space available for caching, at least it allows to limit the space
+on the host for caches, without assuming that elements will do that
+by themselves.
+
+Currently this disk is either required to have a single partition
+on it, or the first partition on it will be used. A disk with
+the latter configuration can be easily created with L<guestfish(1)>
+like the following:
+
+ guestfish -N filename.img=fs:ext4:10G
+
+The above will create a disk image called F<filename.img>, 10G big,
+with a single partition of type ext4;
+see L<guestfish(1)/PREPARED DISK IMAGES>.
+
+It is recommended for it to be E<ge> 5G or even more, as elements
+will cache disk images, distribution packages, etc.
+
+The disk is also used as C<$HOME> during the elements run.
+Virt-dib stores in it the logs of the scripts executed in the appliance.
+
+The drive can be accessed like any other disk image, for example using
+other tools of libguestfs such as L<guestfish(1)>:
+
+ guestfish -a filename.img -m /dev/sda1
+
+If no helper drive is specified with I<--drive>, all the resources
+cached during a virt-dib run will be discarded.
+
+=head1 RAMDISK BUILDING
+
+Virt-dib can emulate also C<ramdisk-image-create>, which is a secondary
+operation mode of C<diskimage-builder>. Instead of being a different
+tool name, virt-dib provides easy access to this mode using the
+I<--ramdisk> switch.
+
+In this mode:
+
+=over 4
+
+=item
+
+there is an additional ramdisk element added (see I<--ramdisk-element>)
+
+=item
+
+no image is produced (so I<--formats> is ignored)
+
+=item
+
+F<$NAME.d> (see I<--name>) will contain initrd, kernel, etc
+
+=back
+
+=head1 TEMPORARY DIRECTORY
+
+Virt-dib uses the standard temporary directory used by libguestfs,
+see L<guestfs(3)/ENVIRONMENT VARIABLES>.
+
+By default this location is F</tmp> (default value for C<$TMPDIR>),
+which on some systems may be on a tmpfs filesystem, and thus
+defaulting to a maximum size of I<half> of physical RAM.
+If virt-dib exceeds this, it may hang or exit early with an error.
+The solution is to point C<$TMPDIR> to a permanent location used
+as temporary location, for example:
+
+ $ mkdir local-tmp
+ $ env TMPDIR=$PWD/local-tmp virt-dib ...
+ $ rm -rf local-tmp
+
+=head1 MACHINE READABLE OUTPUT
+
+The I<--machine-readable> option can be used to make the output more
+machine friendly, which is useful when calling virt-dib from other
+programs, GUIs etc.
+
+Use the option on its own to query the capabilities of the
+virt-dib binary. Typical output looks like this:
+
+ $ virt-dib --machine-readable
+ virt-dib
+ output:qcow2
+ output:tar
+ output:raw
+
+A list of features is printed, one per line, and the program exits
+with status 0.
+
+=head1 COMPARISON WITH DISKIMAGE-BUILDER
+
+Virt-dib is intended as safe replacement for C<diskimage-builder>
+and its C<ramdisk-image-create> mode; the user-notable differences
+consist in:
+
+=over 4
+
+=item
+
+the command line arguments; some of the arguments are the same as
+available in C<diskimage-builder>, while some have different names
+
+=item
+
+the location of non-image output files (like ramdisks and kernels)
+
+=item
+
+the way cached resources are saved: using an helper drive, not directly
+on the disk where virt-dib is run
+
+=item
+
+the need to specify a target size for the output disk, as opposed
+to C<diskimage-builder> calculating an optimal one
+
+=item
+
+the handling of environment variables; see L</ENVIRONMENT VARIABLES>
+
+=back
+
+Elements themselves should notice no difference in they way
+they are run; behaviour differences may due to wrong assumptions in
+elements, or not correct virt-dib emulation.
+
+Known issues at the moment:
+
+=over 4
+
+=item
+
+due to the way C<root.d> elements are run, bind mounts set up to
+share dpkg/yum cache will not work in later phases
+
+=back
+
+=head1 TESTING
+
+Virt-dib has been tested with C<diskimage-builder> (and its elements)
+E<ge> 0.1.41; from time to time also with C<tripleo-image-elements>
+and C<sahara-image-elements>.
+
+Previous versions may work, but it is not guaranteed.
+
+=head1 EXIT STATUS
+
+This program returns 0 if successful, or non-zero if there was an
+error.
+
+=head1 SEE ALSO
+
+L<guestfs(3)>,
+L<guestfish(1)>,
+L<virt-resize(1)>,
+L<http://libguestfs.org/>.
+
+=head1 AUTHOR
+
+Pino Toscano (C<ptoscano at redhat dot com>)
+
+=head1 COPYRIGHT
+
+Copyright (C) 2015 Red Hat Inc.
diff --git a/po-docs/podfiles b/po-docs/podfiles
index c76f1b1..26bd880 100644
--- a/po-docs/podfiles
+++ b/po-docs/podfiles
@@ -11,6 +11,7 @@
../customize/virt-customize.pod
../daemon/guestfsd.pod
../df/virt-df.pod
+../dib/virt-dib.pod
../diff/virt-diff.pod
../edit/virt-edit.pod
../erlang/examples/guestfs-erlang.pod
diff --git a/run.in b/run.in
index 8fdf454..5d30bf4 100755
--- a/run.in
+++ b/run.in
@@ -102,6 +102,7 @@ prepend PATH "$b/sysprep"
prepend PATH "$b/test-tool"
prepend PATH "$b/tools"
prepend PATH "$b/v2v"
+prepend PATH "$b/dib"
export PATH
# Set LD_LIBRARY_PATH and DYLD_LIBRARY_PATH to contain library.
--
2.1.0