Add the possibility to choose which architecture use to build the wanted
image (--arch). Since this implies that running commands on the guest is
usually not possible when the architecture is different than the host
one, another new option (--allow-foreign-arch-ops) allows to run
commands nevertheless.
The caching scheme is adapted to account for the architecture (with
--print-cache showing the architecture as well).
Furthermore, the short output of --list shows the architecture as well,
like the other --list outputs do.
---
builder/Makefile.am | 9 +++++-
builder/architecture.ml | 32 +++++++++++++++++++++
builder/builder.ml | 28 ++++++++++--------
builder/cmdline.ml | 29 +++++++++++++++++--
builder/downloader.ml | 8 +++---
builder/downloader.mli | 10 +++----
builder/index_parser.ml | 22 ++++++++++----
builder/list_entries.ml | 2 ++
builder/test-virt-builder-list.sh | 14 ++++-----
builder/test-virt-builder.sh | 1 +
builder/uname-c.c | 60 +++++++++++++++++++++++++++++++++++++++
builder/uname.ml | 27 ++++++++++++++++++
builder/uname.mli | 28 ++++++++++++++++++
builder/virt-builder.pod | 35 +++++++++++++++++++----
po/POTFILES | 1 +
po/POTFILES-ml | 2 ++
16 files changed, 264 insertions(+), 44 deletions(-)
create mode 100644 builder/architecture.ml
create mode 100644 builder/uname-c.c
create mode 100644 builder/uname.ml
create mode 100644 builder/uname.mli
diff --git a/builder/Makefile.am b/builder/Makefile.am
index 387913c..5a978e3 100644
--- a/builder/Makefile.am
+++ b/builder/Makefile.am
@@ -38,6 +38,7 @@ CLEANFILES = *~ *.cmi *.cmo *.cmx *.cmxa *.o virt-builder
# Alphabetical order.
SOURCES = \
+ architecture.ml \
builder.ml \
cmdline.ml \
downloader.mli \
@@ -61,7 +62,10 @@ SOURCES = \
sigchecker.mli \
sigchecker.ml \
sources.mli \
- sources.ml
+ sources.ml \
+ uname.ml \
+ uname.mli \
+ uname-c.c
man_MANS =
noinst_DATA =
@@ -99,6 +103,9 @@ deps = \
pxzcat.cmx \
setlocale-c.o \
setlocale.cmx \
+ uname-c.o \
+ uname.cmx \
+ architecture.cmx \
ini_reader.cmx \
paths.cmx \
get_kernel.cmx \
diff --git a/builder/architecture.ml b/builder/architecture.ml
new file mode 100644
index 0000000..2aa7e7e
--- /dev/null
+++ b/builder/architecture.ml
@@ -0,0 +1,32 @@
+(* 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.
+ *)
+
+open Common_gettext.Gettext
+open Common_utils
+
+open Unix
+
+let filter_arch = function
+ | "amd64" | "x86_64" | "x64" -> "x86_64"
+ | "powerpc" | "ppc" -> "ppc"
+ | "armhfp" | "armhf" -> "armhf"
+ | arch -> arch
+
+let current_arch =
+ try filter_arch ((Uname.uname ()).Uname.machine)
+ with Unix_error _ -> "unknown"
diff --git a/builder/builder.ml b/builder/builder.ml
index 1800f2d..ed55de1 100644
--- a/builder/builder.ml
+++ b/builder/builder.ml
@@ -38,9 +38,9 @@ let () = Random.self_init ()
let main () =
(* Command line argument parsing - see cmdline.ml. *)
let mode, arg,
- attach, cache, check_signature, curl, debug, delete, delete_on_failure,
- edit, firstboot, run, format, gpg, hostname, install, list_format, links,
- memsize, mkdirs,
+ arch, attach, cache, check_signature, curl, debug, delete,
+ delete_on_failure, edit, firstboot, run, format, gpg, hostname, install,
+ list_format, links, memsize, mkdirs,
network, output, password_crypto, quiet, root_password, scrub,
scrub_logfile, selinux_relabel, size, smp, sources, sync, timezone,
update, upload, writes =
@@ -172,11 +172,11 @@ let main () =
| Some cachedir ->
printf (f_"cache directory: %s\n") cachedir;
List.iter (
- fun (name, { Index_parser.revision = revision; hidden = hidden }) ->
+ fun (name, { Index_parser.revision = revision; arch = arch; hidden = hidden })
->
if not hidden then (
- let filename = Downloader.cache_of_name cachedir name revision in
+ let filename = Downloader.cache_of_name cachedir name arch revision in
let cached = Sys.file_exists filename in
- printf "%-24s %s\n" name
+ printf "%-24s %-10s %s\n" name arch
(if cached then s_"cached" else (*s_*)"no")
)
) index
@@ -193,7 +193,7 @@ let main () =
List.iter (
fun (name,
{ Index_parser.revision = revision; file_uri = file_uri }) ->
- let template = name, revision in
+ let template = name, arch, revision in
msg (f_"Downloading: %s") file_uri;
let progress_bar = not quiet in
ignore (Downloader.download ~prog downloader ~template ~progress_bar
@@ -205,12 +205,16 @@ let main () =
| (`Install|`Notes) as mode -> mode in
(* Which os-version (ie. index entry)? *)
- let entry =
- try List.assoc arg index
+ let item =
+ try List.find (
+ fun (name, { Index_parser.arch = a }) ->
+ name = arg && arch = Architecture.filter_arch a
+ ) index
with Not_found ->
- eprintf (f_"%s: cannot find os-version '%s'.\nUse --list to list
available guest types.\n")
- prog arg;
+ eprintf (f_"%s: cannot find os-version '%s' with architecture
'%s'.\nUse --list to list available guest types.\n")
+ prog arg arch;
exit 1 in
+ let entry = snd item in
let sigchecker = entry.Index_parser.sigchecker in
(match mode with
@@ -234,7 +238,7 @@ let main () =
let template =
let template, delete_on_exit =
let { Index_parser.revision = revision; file_uri = file_uri } = entry in
- let template = arg, revision in
+ let template = arg, arch, revision in
msg (f_"Downloading: %s") file_uri;
let progress_bar = not quiet in
Downloader.download ~prog downloader ~template ~progress_bar file_uri in
diff --git a/builder/cmdline.ml b/builder/cmdline.ml
index 6e8bfd8..0dd0ad2 100644
--- a/builder/cmdline.ml
+++ b/builder/cmdline.ml
@@ -44,6 +44,9 @@ let parse_cmdline () =
let print_cache_mode () = mode := `Print_cache in
let delete_cache_mode () = mode := `Delete_cache in
+ let arch = ref "" in
+ let allow_foreign_arch_ops = ref false in
+
let attach = ref [] in
let attach_format = ref None in
let set_attach_format = function
@@ -221,6 +224,9 @@ let parse_cmdline () =
let ditto = " -\"-" in
let argspec = Arg.align [
+ "--allow-foreign-arch-ops", Arg.Set allow_foreign_arch_ops,
+ " " ^ s_"Allow executing
commands even on non-native output architectures";
+ "--arch", Arg.Set_string arch, "arch" ^ " " ^
s_"Set the output architecture";
"--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";
@@ -319,6 +325,8 @@ read the man page virt-builder(1).
(* Dereference options. *)
let args = List.rev !args in
let mode = !mode in
+ let arch = !arch in
+ let allow_foreign_arch_ops = !allow_foreign_arch_ops in
let attach = List.rev !attach in
let cache = !cache in
let check_signature = !check_signature in
@@ -431,10 +439,25 @@ read the man page virt-builder(1).
(* Combine the sources and fingerprints into a single list of pairs. *)
List.combine sources fingerprints in
+ (* Check the architecture. *)
+ let arch =
+ match arch with
+ | "" -> Architecture.current_arch
+ | arch ->
+ let target_arch = Architecture.filter_arch arch in
+ if Architecture.current_arch <> target_arch && allow_foreign_arch_ops
<> true then (
+ if install <> [] || run <> [] || update then (
+ eprintf (f_"%s: sorry, cannot run commands on a guest with a different
architecture\n")
+ prog;
+ exit 1
+ );
+ );
+ target_arch in
+
mode, arg,
- attach, cache, check_signature, curl, debug, delete, delete_on_failure,
- edit, firstboot, run, format, gpg, hostname, install, list_format, links,
- memsize, mkdirs,
+ arch, attach, cache, check_signature, curl, debug, delete,
+ delete_on_failure, edit, firstboot, run, format, gpg, hostname, install,
+ list_format, links, memsize, mkdirs,
network, output, password_crypto, quiet, root_password, scrub,
scrub_logfile, selinux_relabel, size, smp, sources, sync, timezone,
update, upload, writes
diff --git a/builder/downloader.ml b/builder/downloader.ml
index e386c06..e23cb37 100644
--- a/builder/downloader.ml
+++ b/builder/downloader.ml
@@ -25,8 +25,8 @@ open Printf
let quote = Filename.quote
let (//) = Filename.concat
-let cache_of_name cachedir name revision =
- cachedir // sprintf "%s.%d" name revision
+let cache_of_name cachedir name arch revision =
+ cachedir // sprintf "%s.%s.%d" name arch revision
type uri = string
type filename = string
@@ -51,14 +51,14 @@ let rec download ~prog t ?template ?progress_bar uri =
download_to ~prog t ?progress_bar uri tmpfile;
(tmpfile, true)
- | Some (name, revision) ->
+ | Some (name, arch, revision) ->
match t.cache with
| None ->
(* Not using the cache at all? *)
download t ~prog ?progress_bar uri
| Some cachedir ->
- let filename = cache_of_name cachedir name revision in
+ let filename = cache_of_name cachedir name arch revision in
(* Is the requested template name + revision in the cache already?
* If not, download it.
diff --git a/builder/downloader.mli b/builder/downloader.mli
index 62d500d..7b26ba3 100644
--- a/builder/downloader.mli
+++ b/builder/downloader.mli
@@ -18,7 +18,7 @@
(** This module is a wrapper around curl, plus local caching. *)
-val cache_of_name : string -> string -> int -> string
+val cache_of_name : string -> string -> string -> int -> string
(** [cache_of_name cachedir name revision] returns the filename
of the cached file. (Note: It doesn't check if the filename
exists, this is just a simple string transformation). *)
@@ -32,15 +32,15 @@ type t
val create : debug:bool -> curl:string -> cache:string option -> t
(** Create the abstract type. *)
-val download : prog:string -> t -> ?template:(string*int) -> ?progress_bar:bool
-> uri -> (filename * bool)
+val download : prog:string -> t -> ?template:(string*string*int) ->
?progress_bar:bool -> 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).
+ For templates, you must supply [~template:(name, arch, revision)].
+ This causes the cache to be used (if possible). Name, arch(itecture)
+ and revision are used for cache control (see the man page for details).
If [~progress_bar:true] then display a progress bar if the file
doesn't come from the cache. In debug mode, progress messages
diff --git a/builder/index_parser.ml b/builder/index_parser.ml
index 9fd9cda..472a6c7 100644
--- a/builder/index_parser.ml
+++ b/builder/index_parser.ml
@@ -123,16 +123,26 @@ let get_index ~prog ~debug ~downloader ~sigchecker source =
if delete_tmpfile then
(try Unix.unlink tmpfile with _ -> ());
- (* Check for repeated os-version names. *)
+ (* Check for repeated os-version+arch combination. *)
+ let name_arch_map = List.map (
+ fun (n, fields) ->
+ let rec find_arch = function
+ | ("arch", None, value) :: y -> value
+ | _ :: y -> find_arch y
+ | [] -> ""
+ in
+ n, (find_arch fields)
+ ) sections in
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;
+ fun (n, arch) ->
+ let id = n, arch in
+ if Hashtbl.mem nseen id then (
+ eprintf (f_"virt-builder: index is corrupt: os-version '%s' with
architecture '%s' appears two or more times\n") n arch;
corrupt_file ()
);
- Hashtbl.add nseen n true
- ) sections;
+ Hashtbl.add nseen id true
+ ) name_arch_map;
(* Check for repeated fields. *)
List.iter (
diff --git a/builder/list_entries.ml b/builder/list_entries.ml
index 476bf14..75149e7 100644
--- a/builder/list_entries.ml
+++ b/builder/list_entries.ml
@@ -48,9 +48,11 @@ let rec list_entries ~list_format ~sources index =
and list_entries_short index =
List.iter (
fun (name, { Index_parser.printable_name = printable_name;
+ arch = arch;
hidden = hidden }) ->
if not hidden then (
printf "%-24s" name;
+ printf " %-10s" arch;
(match printable_name with
| None -> ()
| Some s -> printf " %s" s
diff --git a/builder/test-virt-builder-list.sh b/builder/test-virt-builder-list.sh
index 2f9b319..1f62838 100755
--- a/builder/test-virt-builder-list.sh
+++ b/builder/test-virt-builder-list.sh
@@ -27,13 +27,13 @@ export XDG_CONFIG_DIRS="$abs_builddir/test-config"
short_list=$($VG ./virt-builder --no-check-signature --no-cache --list)
-if [ "$short_list" != "phony-debian Phony Debian
-phony-fedora Phony Fedora
-phony-fedora-qcow2 Phony Fedora qcow2
-phony-fedora-qcow2-uncompressed Phony Fedora qcow2 uncompressed
-phony-fedora-no-format Phony Fedora
-phony-ubuntu Phony Ubuntu
-phony-windows Phony Windows" ]; then
+if [ "$short_list" != "phony-debian x86_64 Phony Debian
+phony-fedora x86_64 Phony Fedora
+phony-fedora-qcow2 x86_64 Phony Fedora qcow2
+phony-fedora-qcow2-uncompressed x86_64 Phony Fedora qcow2 uncompressed
+phony-fedora-no-format x86_64 Phony Fedora
+phony-ubuntu x86_64 Phony Ubuntu
+phony-windows x86_64 Phony Windows" ]; then
echo "$0: unexpected --list output:"
echo "$short_list"
exit 1
diff --git a/builder/test-virt-builder.sh b/builder/test-virt-builder.sh
index 85a7888..8841984 100755
--- a/builder/test-virt-builder.sh
+++ b/builder/test-virt-builder.sh
@@ -52,6 +52,7 @@ rm -f $output
$VG ./virt-builder phony-fedora \
-v --no-cache --no-check-signature $no_network \
-o $output --size 2G --format $format \
+ --arch x86_64 --allow-foreign-arch-ops \
--hostname
test.example.com \
--timezone Europe/London \
--root-password password:123456 \
diff --git a/builder/uname-c.c b/builder/uname-c.c
new file mode 100644
index 0000000..b4f2f61
--- /dev/null
+++ b/builder/uname-c.c
@@ -0,0 +1,60 @@
+/* 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.
+ */
+
+/* This file handles the interface between the C/lex/yacc index file
+ * parser, and the OCaml world. See index_parser.ml for the OCaml
+ * type definition.
+ */
+
+#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
+
+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/builder/uname.ml b/builder/uname.ml
new file mode 100644
index 0000000..c370c2c
--- /dev/null
+++ b/builder/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/builder/uname.mli b/builder/uname.mli
new file mode 100644
index 0000000..aea441b
--- /dev/null
+++ b/builder/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/builder/virt-builder.pod b/builder/virt-builder.pod
index 32c0961..708bfd0 100644
--- a/builder/virt-builder.pod
+++ b/builder/virt-builder.pod
@@ -15,6 +15,8 @@ virt-builder - Build virtual machine images quickly
virt-builder os-version
[-o|--output DISKIMAGE] [--size SIZE] [--format raw|qcow2]
+ [--allow-foreign-arch-ops]
+ [--arch ARCHITECTURE]
[--attach ISOFILE]
[--root-password SELECTOR]
[--hostname HOSTNAME]
@@ -81,7 +83,9 @@ are any installation notes:
virt-builder fedora-20
-will build a Fedora 20 image. This will have all default
+will build a Fedora 20 image for the same architecture as virt-builder
+(so running it from an i386 installation will try to build an i386
+image, if available). This will have all default
configuration (minimal size, no user accounts, random root password,
only the bare minimum installed software, etc.).
@@ -109,6 +113,10 @@ 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)>).
+ virt-builder fedora-20 --arch i386
+
+As above, but using an i386 template, if available.
+
=head2 Setting the root password
virt-builder fedora-20 --root-password file:/tmp/rootpw
@@ -189,6 +197,21 @@ You can combine these options, and have multiple options of all
types.
Display help.
+=item B<--allow-foreign-arch-ops>
+
+Forces the execution of commands on the target image even if its
+architecture is different than the current host ones.
+
+See also L</ARCHITECTURE>.
+
+=item B<--arch> ARCHITECTURE
+
+Use the specified architecture for the output image. This means
+there must be sources providing the requested template for the
+requested architecture.
+
+See also L</ARCHITECTURE>.
+
=item B<--attach> ISOFILE
During the customization phase, the given disk is attached to the
@@ -1635,13 +1658,13 @@ highly recommended that you always create signed index and
templates.
Virt-builder can build a guest for any architecture no matter what the
host architecture is. For example an x86-64 guest on an ARM host.
-However certain options may not work correctly, specifically options
+However certain options may not work, specifically options
that require running commands in the guest during the build process:
I<--install>, I<--update>, I<--run>, I<--run-command>. You may
need
-to replace these with their firstboot-equivalents.
-
-An x86-64 host building 32 bit i686 guests should work without any
-special steps.
+to replace these with their firstboot-equivalents, or specify
+I<--allow-foreign-arch-ops> if the target architecture is compatible
+with the host architecture (for example, you are building a 32 bit
+i686 guest on a x86-64 host).
=head2 SECURITY
diff --git a/po/POTFILES b/po/POTFILES
index 5b2ec57..5aeeb2e 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -6,6 +6,7 @@ builder/index-struct.c
builder/index-validate.c
builder/pxzcat-c.c
builder/setlocale-c.c
+builder/uname-c.c
cat/cat.c
cat/filesystems.c
cat/ls.c
diff --git a/po/POTFILES-ml b/po/POTFILES-ml
index 8725e5e..c96b451 100644
--- a/po/POTFILES-ml
+++ b/po/POTFILES-ml
@@ -1,3 +1,4 @@
+builder/architecture.ml
builder/builder.ml
builder/cmdline.ml
builder/downloader.ml
@@ -10,6 +11,7 @@ builder/pxzcat.ml
builder/setlocale.ml
builder/sigchecker.ml
builder/sources.ml
+builder/uname.ml
mllib/common_gettext.ml
mllib/common_utils.ml
mllib/common_utils_tests.ml
--
1.8.3.1