Files
libguestfs/builder/templates/make-template.ml
2019-08-20 16:59:56 +01:00

1550 lines
50 KiB
OCaml
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env ocaml
(* libguestfs
* Copyright (C) 2016-2019 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 script is used to create the virt-builder templates hosted
* http://libguestfs.org/download/builder/
*
* Prior to November 2016, the templates were generated using
* shell scripts located in libguestfs.git/builder/website.
*)
#load "str.cma";;
#load "unix.cma";;
#directory "../../ocaml";; (* use locally built guestfs *)
(*#directory "+guestfs";; (* use globally installed guestfs *) *)
#load "mlguestfs.cma";;
open Printf
let windows_installers = "/mnt/media/installers/Windows"
let prog = "make-template"
(* Ensure that a file is deleted on exit. *)
let unlink_on_exit =
let files = ref [] in
at_exit (
fun () -> List.iter (fun f -> try Unix.unlink f with _ -> ()) !files
);
fun file -> files := file :: !files
let () =
(* Check we are being run from the correct directory. *)
if not (Sys.file_exists "debian.preseed") then (
eprintf "%s: run this script from the builder/templates subdirectory\n"
prog;
exit 1
);
(* Check that the ./run script was used. *)
(try ignore (Sys.getenv "CAML_LD_LIBRARY_PATH")
with Not_found ->
eprintf "%s: you must use `../../run ./make-template.ml ...' to run this script\n"
prog;
exit 1
);
(* Check we're not being run as root. *)
if Unix.geteuid () = 0 then (
eprintf "%s: don't run this script as root\n" prog;
exit 1
);
(* ... and that LIBVIRT_DEFAULT_URI=qemu:///system is NOT set,
* which is the same as above.
*)
let s = try Sys.getenv "LIBVIRT_DEFAULT_URI" with Not_found -> "" in
if s = "qemu:///system" then (
eprintf "%s: don't set LIBVIRT_DEFAULT_URI=qemu:///system\n" prog;
exit 1
)
;;
type os =
| CentOS of int * int (* major, minor *)
| RHEL of int * int
| Debian of int * string (* version, dist name like "wheezy" *)
| Ubuntu of string * string
| Fedora of int (* version number *)
| FreeBSD of int * int (* major, minor *)
| Windows of int * int * windows_variant (* major, minor, variant *)
and windows_variant = Client | Server
type arch = X86_64 | Aarch64 | Armv7 | I686 | PPC64 | PPC64le | S390X
type boot_media =
| Location of string (* virt-install --location (preferred) *)
| CDRom of string (* downloaded CD-ROM *)
let quote = Filename.quote
let (//) = Filename.concat
let rec main () =
assert (Sys.word_size = 64);
Random.self_init ();
(* Parse the command line. *)
let os, arch = parse_cmdline () in
(* Choose a disk size for this OS. *)
let virtual_size_gb = get_virtual_size_gb os arch in
(* For OSes which require a kickstart, this generates one.
* For OSes which require a preseed file, this returns one (we
* don't generate preseed files at the moment).
* For Windows this returns an unattend file in an ISO.
* For OSes which cannot be automated (FreeBSD), this returns None.
*)
let ks = make_kickstart os arch in
(* Find the boot media. Normally virt-install --location but
* for FreeBSD it downloads the boot ISO.
*)
let boot_media = make_boot_media os arch in
(* Choose a random temporary name for the libvirt domain. *)
let tmpname = sprintf "tmp-%s" (random8 ()) in
(* Choose a random temporary disk name. *)
let tmpout = sprintf "%s.img" tmpname in
unlink_on_exit tmpout;
(* Create the final output name (actually not quite final because
* we will xz-compress it).
*)
let output = filename_of_os os arch "" in
(* Some architectures need EFI boot. *)
let tmpefivars =
if needs_uefi os arch then (
let code, vars =
match arch with
| X86_64 ->
"/usr/share/edk2/ovmf/OVMF_CODE.fd",
"/usr/share/edk2/ovmf/OVMF_VARS.fd"
| Aarch64 ->
"/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw",
"/usr/share/edk2/aarch64/vars-template-pflash.raw"
| _ -> assert false in
let vars_out = Sys.getcwd () // sprintf "%s.vars" tmpname in
unlink_on_exit vars_out;
let cmd = sprintf "cp %s %s" (quote vars) (quote vars_out) in
if Sys.command cmd <> 0 then exit 1;
Some (code, vars_out)
)
else None in
(* Now construct the virt-install command. *)
let vi = make_virt_install_command os arch ks tmpname tmpout tmpefivars
boot_media virtual_size_gb in
(* Print the virt-install command just before we run it, because
* this is expected to be long-running.
*)
print_virt_install_command stdout vi;
(* Save the virt-install command to a file, for documentation. *)
let chan = open_out (filename_of_os os arch ".virt-install-cmd") in
fprintf chan "# This is the virt-install command which was used to create\n";
fprintf chan "# the virt-builder template '%s'\n" (string_of_os os arch);
fprintf chan "# NB: This file is generated for documentation purposes ONLY!\n";
fprintf chan "# This script was never run, and is not intended to be run.\n";
fprintf chan "\n";
print_virt_install_command chan vi;
close_out chan;
(* Print the virt-install notes for OSes which cannot be automated
* fully. (These are different from the notes= section in the
* index fragment).
*)
print_install_notes os;
printf "\n\n%!";
(* Run the virt-install command. *)
let pid = Unix.fork () in
if pid = 0 then Unix.execvp "virt-install" vi;
let _, pstat = Unix.waitpid [] pid in
check_process_status_for_errors pstat;
(* If there were NVRAM variables, move them to the final name and
* compress them. Doing this operation later means the cleanup of
* the guest will remove them as well (because of --nvram).
*)
let nvram =
match tmpefivars with
| Some (_, vars) ->
let f = sprintf "%s-nvram" output in
let cmd = sprintf "mv %s %s" (quote vars) (quote f) in
if Sys.command cmd <> 0 then exit 1;
let cmd = sprintf "xz -f --best %s" (quote f) in
if Sys.command cmd <> 0 then exit 1;
Some (f ^ ".xz")
| None -> None in
ignore (Sys.command "sync");
(* Run virt-filesystems, simply to display the filesystems in the image. *)
let cmd = sprintf "virt-filesystems -a %s --all --long -h" (quote tmpout) in
if Sys.command cmd <> 0 then exit 1;
(* Some guests are special flowers that need post-installation
* filesystem changes.
*)
let postinstall = make_postinstall os arch in
(* Get the root filesystem. If the root filesystem is LVM then
* get the partition containing it.
*)
let g = open_guest ~mount:(postinstall <> None) tmpout in
let roots = g#inspect_get_roots () in
let expandfs, lvexpandfs =
let rootfs = g#canonical_device_name roots.(0) in
if String.length rootfs >= 7 && String.sub rootfs 0 7 = "/dev/sd" then
rootfs, None (* non-LVM case *)
else (
(* The LVM case, find the containing partition to expand. *)
let pvs = Array.to_list (g#pvs ()) in
match pvs with
| [pv] ->
let pv = g#canonical_device_name pv in
assert (String.length pv >= 7 && String.sub pv 0 7 = "/dev/sd");
pv, Some rootfs
| [] | _::_::_ -> assert false
) in
(match postinstall with
| None -> ()
| Some f -> f g
);
g#shutdown ();
g#close ();
(match os with
| Ubuntu (ver, _) when ver >= "14.04" ->
(* In Ubuntu >= 14.04 you can't complete the install without creating
* a user account. We create one called 'builder', but we also
* disable it. XXX Combine with virt-sysprep step.
*)
let cmd =
sprintf "virt-customize -a %s --password builder:disabled"
(quote tmpout) in
if Sys.command cmd <> 0 then exit 1
| _ -> ()
);
if can_sysprep_os os then (
(* Sysprep. Relabel SELinux-using guests. *)
printf "Sysprepping ...\n%!";
let cmd =
sprintf "virt-sysprep --quiet -a %s%s"
(quote tmpout)
(if is_selinux_os os then " --selinux-relabel" else "") in
if Sys.command cmd <> 0 then exit 1
);
(* Sparsify and copy to output name. *)
printf "Sparsifying ...\n%!";
let cmd =
sprintf "virt-sparsify --inplace --quiet %s" (quote tmpout) in
if Sys.command cmd <> 0 then exit 1;
(* Move file to final name before compressing. *)
let cmd =
sprintf "mv %s %s" (quote tmpout) (quote output) in
if Sys.command cmd <> 0 then exit 1;
(* Compress the output. *)
printf "Compressing ...\n%!";
let cmd =
sprintf "xz -f --best --block-size=16777216 %s" (quote output) in
if Sys.command cmd <> 0 then exit 1;
let output = output ^ ".xz" in
(* Set public readable permissions on the final file. *)
let cmd = sprintf "chmod 0644 %s" (quote output) in
if Sys.command cmd <> 0 then exit 1;
printf "Template completed: %s\n%!" output;
(* Construct the index fragment, but don't create this for the private
* RHEL images.
*)
(match os with
| RHEL _ -> ()
| _ ->
let index_fragment = filename_of_os os arch ".index-fragment" in
(* If there is an existing file, read the revision and increment it. *)
let revision = read_revision index_fragment in
let revision =
match revision with
(* no existing file *)
| `No_file -> None
(* file exists, but no revision line, so revision=1 *)
| `No_revision -> Some 2
(* existing file with revision line *)
| `Revision i -> Some (i+1) in
make_index_fragment os arch index_fragment output nvram revision
expandfs lvexpandfs virtual_size_gb;
(* Validate the fragment we have just created. *)
let cmd = sprintf "virt-index-validate %s" (quote index_fragment) in
if Sys.command cmd <> 0 then exit 1;
printf "Index fragment created: %s\n" index_fragment
);
printf "Finished successfully.\n%!"
and parse_cmdline () =
let anon = ref [] in
let usage = "\
../../run ./make-template.ml [--options] os version [arch]
Usage:
../../run ./make-template.ml [--options] os version [arch]
Examples:
../../run ./make-template.ml fedora 25
../../run ./make-template.ml rhel 7.3 ppc64le
The arch defaults to x86_64. Note that i686 is treated as a
separate arch.
Options:
" in
let spec = Arg.align [
] in
Arg.parse spec (fun s -> anon := s :: !anon) usage;
let os, ver, arch =
match List.rev !anon with
| [os; ver] -> os, ver, "x86_64"
| [os; ver; arch] -> os, ver, arch
| _ ->
eprintf "%s [--options] os version [arch]\n" prog;
exit 1 in
let os = os_of_string os ver
and arch = arch_of_string arch in
os, arch
and os_of_string os ver =
match os, ver with
| "centos", ver -> let maj, min = parse_major_minor ver in CentOS (maj, min)
| "rhel", ver -> let maj, min = parse_major_minor ver in RHEL (maj, min)
| "debian", "6" -> Debian (6, "squeeze")
| "debian", "7" -> Debian (7, "wheezy")
| "debian", "8" -> Debian (8, "jessie")
| "debian", "9" -> Debian (9, "stretch")
| "debian", "10" -> Debian (10, "buster")
| "ubuntu", "10.04" -> Ubuntu (ver, "lucid")
| "ubuntu", "12.04" -> Ubuntu (ver, "precise")
| "ubuntu", "14.04" -> Ubuntu (ver, "trusty")
| "ubuntu", "16.04" -> Ubuntu (ver, "xenial")
| "ubuntu", "18.04" -> Ubuntu (ver, "bionic")
| "fedora", ver -> Fedora (int_of_string ver)
| "freebsd", ver -> let maj, min = parse_major_minor ver in FreeBSD (maj, min)
| "windows", ver -> parse_windows_version ver
| _ ->
eprintf "%s: unknown or unsupported OS (%s, %s)\n" prog os ver; exit 1
and parse_major_minor ver =
let rex = Str.regexp "^\\([0-9]+\\)\\.\\([0-9]+\\)$" in
if Str.string_match rex ver 0 then (
int_of_string (Str.matched_group 1 ver),
int_of_string (Str.matched_group 2 ver)
)
else (
eprintf "%s: cannot parse major.minor (%s)\n" prog ver;
exit 1
)
(* https://en.wikipedia.org/wiki/List_of_Microsoft_Windows_versions *)
and parse_windows_version = function
| "7" -> Windows (6, 1, Client)
| "2k8r2" -> Windows (6, 1, Server)
| "2k12" -> Windows (6, 2, Server)
| "2k12r2" -> Windows (6, 3, Server)
| "2k16" -> Windows (10, 0, Server)
| _ ->
eprintf "%s: cannot parse Windows version, see parse_windows_version\n"
prog;
exit 1
and arch_of_string = function
| "x86_64" -> X86_64
| "aarch64" -> Aarch64
| "armv7l" -> Armv7
| "i686" -> I686
| "ppc64" -> PPC64
| "ppc64le" -> PPC64le
| "s390x" -> S390X
| s ->
eprintf "%s: unknown or unsupported arch (%s)\n" prog s; exit 1
and string_of_arch = function
| X86_64 -> "x86_64"
| Aarch64 -> "aarch64"
| Armv7 -> "armv7l"
| I686 -> "i686"
| PPC64 -> "ppc64"
| PPC64le -> "ppc64le"
| S390X -> "s390x"
and debian_arch_of_arch = function
| X86_64 -> "amd64"
| Aarch64 -> "arm64"
| Armv7 -> "armhf"
| I686 -> "i386"
| PPC64 -> "ppc64"
| PPC64le -> "ppc64el"
| S390X -> "s390x"
and filename_of_os os arch ext =
match os with
| Fedora ver ->
if arch = X86_64 then sprintf "fedora-%d%s" ver ext
else sprintf "fedora-%d-%s%s" ver (string_of_arch arch) ext
| CentOS (major, minor) ->
if arch = X86_64 then sprintf "centos-%d.%d%s" major minor ext
else sprintf "centos-%d.%d-%s%s" major minor (string_of_arch arch) ext
| RHEL (major, minor) ->
if arch = X86_64 then sprintf "rhel-%d.%d%s" major minor ext
else sprintf "rhel-%d.%d-%s%s" major minor (string_of_arch arch) ext
| Debian (ver, _) ->
if arch = X86_64 then sprintf "debian-%d%s" ver ext
else sprintf "debian-%d-%s%s" ver (string_of_arch arch) ext
| Ubuntu (ver, _) ->
if arch = X86_64 then sprintf "ubuntu-%s%s" ver ext
else sprintf "ubuntu-%s-%s%s" ver (string_of_arch arch) ext
| FreeBSD (major, minor) ->
if arch = X86_64 then sprintf "freebsd-%d.%d%s" major minor ext
else sprintf "freebsd-%d.%d-%s%s" major minor (string_of_arch arch) ext
| Windows (major, minor, Client) ->
if arch = X86_64 then sprintf "windows-%d.%d-client%s" major minor ext
else sprintf "windows-%d.%d-client-%s%s"
major minor (string_of_arch arch) ext
| Windows (major, minor, Server) ->
if arch = X86_64 then sprintf "windows-%d.%d-server%s" major minor ext
else sprintf "windows-%d.%d-server-%s%s"
major minor (string_of_arch arch) ext
and string_of_os os arch = filename_of_os os arch ""
(* This is what virt-builder called "os-version". *)
and string_of_os_noarch = function
| Fedora ver -> sprintf "fedora-%d" ver
| CentOS (major, minor) -> sprintf "centos-%d.%d" major minor
| RHEL (major, minor) -> sprintf "rhel-%d.%d" major minor
| Debian (ver, _) -> sprintf "debian-%d" ver
| Ubuntu (ver, _) -> sprintf "ubuntu-%s" ver
| FreeBSD (major, minor) -> sprintf "freebsd-%d.%d" major minor
| Windows (major, minor, Client) -> sprintf "windows-%d.%d-client" major minor
| Windows (major, minor, Server) -> sprintf "windows-%d.%d-server" major minor
(* Does virt-sysprep know how to sysprep this OS? *)
and can_sysprep_os = function
| RHEL _ | CentOS _ | Fedora _ | Debian _ | Ubuntu _ -> true
| FreeBSD _ | Windows _ -> false
and is_selinux_os = function
| RHEL _ | CentOS _ | Fedora _ -> true
| Debian _ | Ubuntu _
| FreeBSD _ | Windows _ -> false
and needs_uefi os arch =
match os, arch with
| Fedora _, Aarch64
| RHEL _, Aarch64 -> true
| RHEL _, _ | CentOS _, _ | Fedora _, _
| Debian _, _ | Ubuntu _, _
| FreeBSD _, _ | Windows _, _ -> false
and get_virtual_size_gb os arch =
match os with
| RHEL _ | CentOS _ | Fedora _
| Debian _ | Ubuntu _
| FreeBSD _ -> 6
| Windows (10, _, _) -> 40 (* Windows 10 *)
| Windows (6, _, _) -> 10 (* Windows from 2008 - 2012 *)
| Windows (5, _, _) -> 6 (* Windows <= 2003 *)
| Windows _ -> assert false
and make_kickstart os arch =
match os with
(* Kickstart. *)
| Fedora _ | CentOS _ | RHEL _ ->
let ks_filename = filename_of_os os arch ".ks" in
Some (make_kickstart_common ks_filename os arch)
(* Preseed. *)
| Debian _ -> Some (copy_preseed_to_temporary "debian.preseed")
| Ubuntu _ -> Some (copy_preseed_to_temporary "ubuntu.preseed")
(* Not automated. *)
| FreeBSD _ -> None
(* Windows unattend.xml wrapped in an ISO. *)
| Windows _ -> Some (make_unattend_iso os arch)
and make_kickstart_common ks_filename os arch =
let buf = Buffer.create 4096 in
let bpf fs = bprintf buf fs in
bpf "\
# Kickstart file for %s
# Generated by libguestfs.git/builder/templates/make-template.ml
install
text
reboot
lang en_US.UTF-8
keyboard us
network --bootproto dhcp
rootpw builder
firewall --enabled --ssh
timezone --utc America/New_York
" (string_of_os os arch);
(match os with
| RHEL (ver, _) when ver <= 4 ->
bpf "\
langsupport en_US
mouse generic
";
| _ -> ()
);
(match os with
| RHEL (3, _) -> ()
| _ ->
bpf "selinux --enforcing\n"
);
(match os with
| RHEL (5, _) -> bpf "key --skip\n"
| _ -> ()
);
bpf "\n";
bpf "bootloader --location=mbr --append=\"%s\"\n"
(kernel_cmdline_of_os os arch);
bpf "\n";
(match os with
| CentOS ((3|4|5|6) as major, _) | RHEL ((3|4|5|6) as major, _) ->
let bootfs = if major <= 5 then "ext2" else "ext4" in
let rootfs = if major <= 4 then "ext3" else "ext4" in
bpf "\
zerombr
clearpart --all --initlabel
part /boot --fstype=%s --size=512 --asprimary
part swap --size=1024 --asprimary
part / --fstype=%s --size=1024 --grow --asprimary
" bootfs rootfs;
| CentOS _ | RHEL _ | Fedora _ ->
bpf "\
zerombr
clearpart --all --initlabel --disklabel=gpt
autopart --type=plain
";
| _ -> assert false (* cannot happen, see caller *)
);
bpf "\n";
(match os with
| RHEL (3, _) -> ()
| _ ->
bpf "\
# Halt the system once configuration has finished.
poweroff
";
);
bpf "\n";
bpf "\
%%packages
@core
";
(match os with
| RHEL ((3|4|5), _) -> ()
| _ ->
bpf "%%end\n"
);
bpf "\n";
(* Generate the %post script section. The previous scripts did
* many different things here. The current script tries to update
* the packages and enable Xen drivers only.
*)
let regenerate_dracut () =
bpf "\
# To make dracut config changes permanent, we need to rerun dracut.
# Rerun dracut for the installed kernel (not the running kernel).
# See commit 0fa52e4e45d80874bc5ea5f112f74be1d3f3472f and
# https://www.redhat.com/archives/libguestfs/2014-June/thread.html#00045
KERNEL_VERSION=\"$(rpm -q kernel --qf '%%{version}-%%{release}.%%{arch}\\n' | sort -V | tail -1)\"
dracut -f /boot/initramfs-$KERNEL_VERSION.img $KERNEL_VERSION
"
in
(match os with
| Fedora _ ->
bpf "%%post\n";
bpf "\
# Ensure the installation is up-to-date.
dnf -y --best upgrade
";
let needs_regenerate_dracut = ref false in
if arch = X86_64 then (
bpf "\
# Enable Xen domU support.
pushd /etc/dracut.conf.d
echo 'add_drivers+=\" xen:vbd xen:vif \"' > virt-builder-xen-drivers.conf
popd
";
needs_regenerate_dracut := true
);
if arch = PPC64 || arch = PPC64le then (
bpf "\
# Enable virtio-scsi support.
pushd /etc/dracut.conf.d
echo 'add_drivers+=\" virtio-blk virtio-scsi \"' > virt-builder-virtio-scsi.conf
popd
";
needs_regenerate_dracut := true
);
if !needs_regenerate_dracut then regenerate_dracut ();
bpf "%%end\n\n"
| RHEL (7,_) ->
bpf "%%post\n";
let needs_regenerate_dracut = ref false in
if arch = PPC64 || arch = PPC64le then (
bpf "\
# Enable virtio-scsi support.
pushd /etc/dracut.conf.d
echo 'add_drivers+=\" virtio-blk virtio-scsi \"' > virt-builder-virtio-scsi.conf
popd
";
needs_regenerate_dracut := true
);
if !needs_regenerate_dracut then regenerate_dracut ();
bpf "%%end\n\n"
| _ -> ()
);
bpf "# EOF\n";
(* Write out the kickstart file. *)
let chan = open_out (ks_filename ^ ".new") in
Buffer.output_buffer chan buf;
close_out chan;
let cmd =
sprintf "mv %s %s" (quote (ks_filename ^ ".new")) (quote ks_filename) in
if Sys.command cmd <> 0 then exit 1;
(* Return the kickstart filename. *)
ks_filename
and copy_preseed_to_temporary source =
(* d-i only works if the file is literally called "/preseed.cfg" *)
let d = Filename.get_temp_dir_name () // random8 () ^ ".tmp" in
let f = d // "preseed.cfg" in
Unix.mkdir d 0o700;
let cmd = sprintf "cp %s %s" (quote source) (quote f) in
if Sys.command cmd <> 0 then exit 1;
f
(* For Windows:
* https://serverfault.com/questions/644437/unattended-installation-of-windows-server-2012-on-kvm
*)
and make_unattend_iso os arch =
printf "enter Windows product key: ";
let product_key = read_line () in
let output_iso =
Sys.getcwd () // filename_of_os os arch "-unattend.iso" in
unlink_on_exit output_iso;
let d = Filename.get_temp_dir_name () // random8 () in
Unix.mkdir d 0o700;
let config_dir = d // "config" in
Unix.mkdir config_dir 0o700;
let f = config_dir // "autounattend.xml" in
let chan = open_out f in
let arch =
match arch with
| X86_64 -> "amd64"
| I686 -> "x86"
| _ ->
eprintf "%s: Windows architecture %s not supported\n"
prog (string_of_arch arch);
exit 1 in
(* Tip: If the install fails with a useless error "The answer file is
* invalid", type Shift + F10 into the setup screen and look for a
* file called \Windows\Panther\Setupact.log (NB:
* not \Windows\Setupact.log)
*)
fprintf chan "
<unattend xmlns=\"urn:schemas-microsoft-com:unattend\"
xmlns:ms=\"urn:schemas-microsoft-com:asm.v3\"
xmlns:wcm=\"http://schemas.microsoft.com/WMIConfig/2002/State\">
<settings pass=\"windowsPE\">
<component name=\"Microsoft-Windows-Setup\"
publicKeyToken=\"31bf3856ad364e35\"
language=\"neutral\"
versionScope=\"nonSxS\"
processorArchitecture=\"%s\">
<UserData>
<AcceptEula>true</AcceptEula>
<ProductKey>
<Key>%s</Key>
<WillShowUI>OnError</WillShowUI>
</ProductKey>
</UserData>
<DiskConfiguration>
<Disk wcm:action=\"add\">
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
<CreatePartitions>
<!-- System partition -->
<CreatePartition wcm:action=\"add\">
<Order>1</Order>
<Type>Primary</Type>
<Size>300</Size>
</CreatePartition>
<!-- Windows partition -->
<CreatePartition wcm:action=\"add\">
<Order>2</Order>
<Type>Primary</Type>
<Extend>true</Extend>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<!-- System partition -->
<ModifyPartition wcm:action=\"add\">
<Order>1</Order>
<PartitionID>1</PartitionID>
<Label>System</Label>
<Format>NTFS</Format>
<Active>true</Active>
</ModifyPartition>
<!-- Windows partition -->
<ModifyPartition wcm:action=\"add\">
<Order>2</Order>
<PartitionID>2</PartitionID>
<Label>Windows</Label>
<Letter>C</Letter>
<Format>NTFS</Format>
</ModifyPartition>
</ModifyPartitions>
</Disk>
<WillShowUI>OnError</WillShowUI>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<WillShowUI>Never</WillShowUI>
<InstallFrom>
<MetaData>
<Key>/IMAGE/INDEX</Key>
<Value>1</Value>
</MetaData>
</InstallFrom>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>2</PartitionID>
</InstallTo>
</OSImage>
</ImageInstall>
</component>
<component name=\"Microsoft-Windows-International-Core-WinPE\"
publicKeyToken=\"31bf3856ad364e35\"
language=\"neutral\"
versionScope=\"nonSxS\"
processorArchitecture=\"%s\">
<SetupUILanguage>
<UILanguage>en-US</UILanguage>
</SetupUILanguage>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
</settings>
</unattend>"
arch product_key arch;
close_out chan;
let cmd = sprintf "cd %s && mkisofs -o %s -J -r config"
(quote d) (quote output_iso) in
if Sys.command cmd <> 0 then exit 1;
let cmd = sprintf "rm -rf %s" (quote d) in
if Sys.command cmd <> 0 then exit 1;
(* Return the name of the unattend ISO. *)
output_iso
and make_boot_media os arch =
match os, arch with
| CentOS (major, _), Aarch64 ->
(* XXX This always points to the latest CentOS, so
* effectively the minor number is always ignored.
*)
Location (sprintf "http://mirror.centos.org/altarch/%d/os/aarch64/"
major)
| CentOS (major, _), X86_64 ->
(* For 6.x we rebuild this every time there is a new 6.x release, and bump
* the revision in the index.
* For 7.x this always points to the latest CentOS, so
* effectively the minor number is always ignored.
*)
Location (sprintf "http://mirror.centos.org/centos-7/%d/os/x86_64/"
major)
| Debian (_, dist), arch ->
Location (sprintf "http://deb.debian.org/debian/dists/%s/main/installer-%s"
dist (debian_arch_of_arch arch))
(* Fedora primary architectures. *)
| Fedora ver, Armv7 ->
Location (sprintf "http://mirror.bytemark.co.uk/fedora/linux/releases/%d/Server/armhfp/os/" ver)
| Fedora ver, X86_64 when ver < 21 ->
Location (sprintf "http://mirror.bytemark.co.uk/fedora/linux/releases/%d/Fedora/x86_64/os/" ver)
| Fedora ver, X86_64 ->
Location (sprintf "http://mirror.bytemark.co.uk/fedora/linux/releases/%d/Server/x86_64/os/" ver)
| Fedora ver, Aarch64 ->
Location (sprintf "http://mirror.bytemark.co.uk/fedora/linux/releases/%d/Server/aarch64/os/" ver)
(* Fedora secondary architectures.
* By using dl.fedoraproject.org we avoid randomly using mirrors
* which might have incomplete copies.
*)
| Fedora ver, I686 ->
Location (sprintf "https://dl.fedoraproject.org/pub/fedora-secondary/releases/%d/Server/i386/os/" ver)
| Fedora ver, PPC64 ->
Location (sprintf "https://dl.fedoraproject.org/pub/fedora-secondary/releases/%d/Server/ppc64/os/" ver)
| Fedora ver, PPC64le ->
Location (sprintf "https://dl.fedoraproject.org/pub/fedora-secondary/releases/%d/Server/ppc64le/os/" ver)
| Fedora ver, S390X ->
Location (sprintf "https://dl.fedoraproject.org/pub/fedora-secondary/releases/%d/Server/s390x/os/" ver)
| RHEL (3, minor), X86_64 ->
Location (sprintf "http://download.devel.redhat.com/released/RHEL-3/U%d/AS/x86_64/tree" minor)
| RHEL (4, minor), X86_64 ->
Location (sprintf "http://download.devel.redhat.com/released/RHEL-4/U%d/AS/x86_64/tree" minor)
| RHEL (5, minor), I686 ->
Location (sprintf "http://download.devel.redhat.com/released/RHEL-5-Server/U%d/i386/os" minor)
| RHEL (5, minor), X86_64 ->
Location (sprintf "http://download.devel.redhat.com/released/RHEL-5-Server/U%d/x86_64/os" minor)
| RHEL (6, minor), I686 ->
Location (sprintf "http://download.devel.redhat.com/released/RHEL-6/6.%d/Server/i386/os" minor)
| RHEL (6, minor), X86_64 ->
Location (sprintf "http://download.devel.redhat.com/released/RHEL-6/6.%d/Server/x86_64/os" minor)
| RHEL (7, minor), X86_64 ->
Location (sprintf "http://download.devel.redhat.com/released/RHEL-7/7.%d/Server/x86_64/os" minor)
| RHEL (7, minor), PPC64 ->
Location (sprintf "http://download.devel.redhat.com/released/RHEL-7/7.%d/Server/ppc64/os" minor)
| RHEL (7, minor), PPC64le ->
Location (sprintf "http://download.devel.redhat.com/released/RHEL-7/7.%d/Server/ppc64le/os" minor)
| RHEL (7, minor), S390X ->
Location (sprintf "http://download.devel.redhat.com/released/RHEL-7/7.%d/Server/s390x/os" minor)
| RHEL (7, minor), Aarch64 ->
Location (sprintf "http://download.eng.bos.redhat.com/released/RHEL-ALT-7/7.%d/Server/aarch64/os" minor)
| RHEL (8, minor), arch ->
Location (sprintf "http://download.eng.bos.redhat.com/released/RHEL-8/8.%d.0/BaseOS/%s/os" minor (string_of_arch arch))
| Ubuntu (_, dist), X86_64 ->
Location (sprintf "http://archive.ubuntu.com/ubuntu/dists/%s/main/installer-amd64" dist)
| Ubuntu (_, dist), PPC64le ->
Location (sprintf "http://ports.ubuntu.com/ubuntu-ports/dists/%s/main/installer-ppc64el" dist)
| FreeBSD (major, minor), X86_64 ->
let iso = sprintf "FreeBSD-%d.%d-RELEASE-amd64-disc1.iso"
major minor in
let iso_xz = sprintf "ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/amd64/ISO-IMAGES/%d.%d/%s.xz"
major minor iso in
let cmd = sprintf "wget -nc %s" (quote iso_xz) in
if Sys.command cmd <> 0 then exit 1;
let cmd = sprintf "unxz -f --keep %s.xz" iso in
if Sys.command cmd <> 0 then exit 1;
CDRom iso
| Windows (major, minor, variant), arch ->
let iso_name =
match major, minor, variant, arch with
| 6, 1, Client, X86_64 -> (* Windows 7 *)
"en_windows_7_ultimate_with_sp1_x64_dvd_u_677332.iso"
| 6, 1, Server, X86_64 -> (* Windows 2008 R2 *)
"en_windows_server_2008_r2_with_sp1_x64_dvd_617601.iso"
| 6, 2, Server, X86_64 -> (* Windows Server 2012 *)
"en_windows_server_2012_x64_dvd_915478.iso"
| 6, 3, Server, X86_64 -> (* Windows Server 2012 R2 *)
"en_windows_server_2012_r2_with_update_x64_dvd_6052708.iso"
| 10, 0, Server, X86_64 -> (* Windows Server 2016 *)
"en_windows_server_2016_updated_feb_2018_x64_dvd_11636692.iso"
| _ ->
eprintf "%s: don't have an installer ISO for this version of Windows\n"
prog;
exit 1 in
CDRom (windows_installers // iso_name)
| _ ->
eprintf "%s: don't know how to calculate the --location for this OS and architecture\n" prog;
exit 1
and print_install_notes = function
| Ubuntu _ ->
printf "\
Some preseed functions are not automated. You may need to hit [Return]
a few times during the install.\n"
| FreeBSD _ ->
printf "\
The FreeBSD install is not automated. Select all defaults, except:
- root password: builder
- timezone: UTC
- do not add any user accounts\n"
| _ -> ()
(* If the install is not automated and we need a graphical console. *)
and needs_graphics = function
| CentOS _ | RHEL _ | Debian _ | Ubuntu _ | Fedora _ -> false
| FreeBSD _ | Windows _ -> true
(* NB: Arguments do not need to be quoted, because we pass them
* directly to exec(2).
*)
and make_virt_install_command os arch ks tmpname tmpout tmpefivars
boot_media virtual_size_gb =
let args = ref [] in
let add arg = args := arg :: !args in
add "virt-install";
(* This ensures the libvirt domain will be automatically deleted
* when virt-install exits. However it doesn't work for certain
* types of guest.
*)
(match os with
| Windows _ ->
printf "after Windows has installed, do:\n";
printf " virsh shutdown %s\n virsh undefine %s\n%!" tmpname tmpname;
| _ -> add "--transient"
);
(* Don't try relabelling everything. This is particularly necessary
* for the Windows install ISOs which are located on NFS.
*)
(match os with
| Windows _ -> add "--security=type=none"
| _ -> ()
);
add (sprintf "--name=%s" tmpname);
(*add "--print-xml";*)
(match arch with
| PPC64 | PPC64le -> add "--ram=4096"
| _ -> add "--ram=2048"
);
(match arch with
| X86_64 ->
add "--arch=x86_64";
add "--cpu=host";
add "--vcpus=4"
| PPC64 ->
add "--arch=ppc64";
add "--machine=pseries";
add "--cpu=power7";
add "--vcpus=1"
| PPC64le ->
add "--arch=ppc64le";
add "--machine=pseries";
add "--cpu=power8";
add "--vcpus=1"
| arch ->
add (sprintf "--arch=%s" (string_of_arch arch));
add "--vcpus=1"
);
add (sprintf "--os-variant=%s" (os_variant_of_os ~for_fedora:true os arch));
(match tmpefivars with
| Some (code, vars) ->
add "--boot";
add (sprintf "loader=%s,loader_ro=yes,loader_type=pflash,nvram=%s"
code vars)
| _ -> ()
);
(* --initrd-inject and --extra-args flags for Linux only. *)
(match os with
| Debian _ | Ubuntu _
| Fedora _ | RHEL _ | CentOS _ ->
let ks =
match ks with None -> assert false | Some ks -> ks in
add (sprintf "--initrd-inject=%s" ks);
let os_extra =
match os with
| Debian _ | Ubuntu _ -> "auto"
| Fedora _ | RHEL _ | CentOS _ ->
sprintf "ks=file:/%s" (Filename.basename ks)
| FreeBSD _ | Windows _ -> assert false in
let proxy =
let p = try Some (Sys.getenv "http_proxy") with Not_found -> None in
match p with
| None ->
(match os with
| Fedora _ | RHEL _ | CentOS _ | Ubuntu _ -> ""
| Debian _ -> "mirror/http/proxy="
| FreeBSD _ | Windows _ -> assert false
)
| Some p ->
match os with
| Fedora _ | RHEL _ | CentOS _ -> "proxy=" ^ p
| Debian _ | Ubuntu _ -> "mirror/http/proxy=" ^ p
| FreeBSD _ | Windows _ -> assert false in
add (sprintf "--extra-args=%s %s %s" (* sic: does NOT need to be quoted *)
os_extra proxy (kernel_cmdline_of_os os arch));
(* doesn't need --initrd-inject *)
| FreeBSD _ | Windows _ -> ()
);
add (sprintf "--disk=%s,size=%d,format=raw"
(Sys.getcwd () // tmpout) virtual_size_gb);
(match boot_media with
| Location location -> add (sprintf "--location=%s" location)
| CDRom iso -> add (sprintf "--disk=%s,device=cdrom,boot_order=1" iso)
);
(* Windows requires one or two extra CDs!
* See: https://serverfault.com/questions/644437/unattended-installation-of-windows-server-2012-on-kvm
*)
(match os with
| Windows _ ->
let unattend_iso =
match ks with None -> assert false | Some ks -> ks in
(*add "--disk=/usr/share/virtio-win/virtio-win.iso,device=cdrom,boot_order=98";*)
add (sprintf "--disk=%s,device=cdrom,boot_order=99" unattend_iso)
| _ -> ()
);
add "--serial=pty";
if not (needs_graphics os) then add "--nographics";
(* Return the command line (list of arguments). *)
Array.of_list (List.rev !args)
and print_virt_install_command chan vi =
Array.iter (
fun arg ->
if arg.[0] = '-' then fprintf chan "\\\n %s " (quote arg)
else fprintf chan "%s " (quote arg)
) vi;
fprintf chan "\n\n%!"
(* The optional [?for_fedora] flag means that we only return
* libosinfo data as currently supported by the latest version of
* Fedora.
*
* This is because if you try to use [virt-install --os-variant=...]
* with an os-variant which the host doesn't support, it won't work,
* and I currently use Fedora, so whatever is supported there matters.
*)
and os_variant_of_os ?(for_fedora = false) os arch =
if not for_fedora then (
match os with
| Fedora ver -> sprintf "fedora%d" ver
| CentOS (major, minor) -> sprintf "centos%d.%d" major minor
| RHEL (major, minor) -> sprintf "rhel%d.%d" major minor
| Debian (ver, _) -> sprintf "debian%d" ver
| Ubuntu (ver, _) -> sprintf "ubuntu%s" ver
| FreeBSD (major, minor) -> sprintf "freebsd%d.%d" major minor
| Windows (6, 1, Client) -> "win7"
| Windows (6, 1, Server) -> "win2k8r2"
| Windows (6, 2, Server) -> "win2k12"
| Windows (6, 3, Server) -> "win2k12r2"
| Windows (10, 0, Server) -> "win2k16"
| Windows _ -> assert false
)
else (
match os, arch with
(* This special case for Fedora/ppc64{,le} is needed to work
* around a bug in virt-install:
* https://bugzilla.redhat.com/show_bug.cgi?id=1399083
*)
| Fedora _, (PPC64|PPC64le) -> "fedora22"
| Fedora ver, _ when ver <= 23 ->
sprintf "fedora%d" ver
| Fedora _, _ -> "fedora26" (* max version known in Fedora 28 *)
| CentOS (major, minor), _ when (major, minor) <= (7,0) ->
sprintf "centos%d.%d" major minor
| CentOS _, _ -> "centos7.0" (* max version known in Fedora 24 *)
| RHEL (6, minor), _ when minor <= 8 ->
sprintf "rhel6.%d" minor
| RHEL (6, _), _ -> "rhel6.9" (* max version known in Fedora 29 *)
| RHEL (7, minor), _ when minor <= 4 ->
sprintf "rhel7.%d" minor
| RHEL (7, _), _ -> "rhel7.5" (* max version known in Fedora 29 *)
| RHEL (major, minor), _ ->
sprintf "rhel%d.%d" major minor
| Debian (ver, _), _ when ver <= 8 -> sprintf "debian%d" ver
| Debian _, _ -> "debian8" (* max version known in Fedora 26 *)
| Ubuntu (ver, _), _ when ver < "18.04" -> sprintf "ubuntu%s" ver
| Ubuntu ("18.04", _), _ -> "ubuntu17.04"
| Ubuntu _, _ -> assert false
| FreeBSD (major, minor), _ -> sprintf "freebsd%d.%d" major minor
| Windows (6, 1, Client), _ -> "win7"
| Windows (6, 1, Server), _ -> "win2k8r2"
| Windows (6, 2, Server), _ -> "win2k12"
| Windows (6, 3, Server), _ -> "win2k12r2"
| Windows (10, 0, Server), _ -> "win2k16"
| Windows _, _ -> assert false
)
and kernel_cmdline_of_os os arch =
match os, arch with
| _, X86_64
| _, I686
| _, S390X ->
"console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
| _, Aarch64 ->
"console=ttyAMA0 earlyprintk=pl011,0x9000000 ignore_loglevel no_timer_check printk.time=1 rd_NO_PLYMOUTH"
| _, Armv7 ->
"console=tty0 console=ttyAMA0,115200 rd_NO_PLYMOUTH"
| (Debian _|Fedora _|Ubuntu _), (PPC64|PPC64le) ->
"console=tty0 console=hvc0 rd_NO_PLYMOUTH"
| (RHEL _|CentOS _), PPC64
| (RHEL _|CentOS _), PPC64le ->
"console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH"
| FreeBSD _, _ | Windows _, _ -> assert false
and make_postinstall os arch =
match os with
| Debian _ | Ubuntu _ ->
Some (
fun g ->
(* Remove apt proxy configuration (thanks: Daniel Miranda). *)
g#rm_f "/etc/apt/apt.conf";
g#touch "/etc/apt/apt.conf"
)
| RHEL (major, minor) when major >= 5 ->
Some (
fun g ->
(* RHEL guests require alternate yum configuration pointing to
* Red Hat's internal servers.
*)
let yum_conf = make_rhel_yum_conf major minor arch in
g#write "/etc/yum.repos.d/download.devel.redhat.com.repo" yum_conf
)
| RHEL _ | Fedora _ | CentOS _ | FreeBSD _ | Windows _ -> None
and make_rhel_yum_conf major minor arch =
let buf = Buffer.create 4096 in
let bpf fs = bprintf buf fs in
if major <= 8 then (
let baseurl, srpms, optional =
match major, arch with
| 5, (I686|X86_64) ->
let arch = match arch with I686 -> "i386" | _ -> string_of_arch arch in
let topurl =
sprintf "http://download.devel.redhat.com/released/RHEL-5-Server/U%d"
minor in
sprintf "%s/%s/os/Server" topurl arch,
sprintf "%s/source/SRPMS" topurl,
None
| 6, (I686|X86_64) ->
let arch = match arch with I686 -> "i386" | _ -> string_of_arch arch in
let topurl =
sprintf "http://download.devel.redhat.com/released/RHEL-%d/%d.%d"
major major minor in
sprintf "%s/Server/%s/os" topurl arch,
sprintf "%s/source/SRPMS" topurl,
Some (sprintf "%s/Server/optional/%s/os" arch topurl,
sprintf "%s/Server/optional/source/SRPMS" topurl)
| 7, (X86_64|PPC64|PPC64le|S390X) ->
let topurl =
sprintf "http://download.devel.redhat.com/released/RHEL-%d/%d.%d"
major major minor in
sprintf "%s/Server/%s/os" topurl (string_of_arch arch),
sprintf "%s/Server/source/tree" topurl,
Some (sprintf "%s/Server-optional/%s/os" topurl (string_of_arch arch),
sprintf "%s/Server-optional/source/tree" topurl)
| 7, Aarch64 ->
let topurl =
sprintf "http://download.devel.redhat.com/released/RHEL-ALT-%d/%d.%d"
major major minor in
sprintf "%s/Server/%s/os" topurl (string_of_arch arch),
sprintf "%s/Server/source/tree" topurl,
Some (sprintf "%s/Server-optional/%s/os" topurl (string_of_arch arch),
sprintf "%s/Server-optional/source/tree" topurl)
| 8, arch ->
let topurl =
sprintf "http://download.devel.redhat.com/released/RHEL-%d/%d.%d.0"
major major minor in
sprintf "%s/BaseOS/%s/os" topurl (string_of_arch arch),
sprintf "%s/BaseOS/source/tree" topurl,
None (* XXX sort out AppStream and CRB *)
| _ -> assert false in
bpf "\
# Yum configuration pointing to Red Hat servers.
[rhel%d]
name=RHEL %d Server
baseurl=%s
enabled=1
gpgcheck=0
keepcache=0
[rhel%d-source]
name=RHEL %d Server Source
baseurl=%s
enabled=0
gpgcheck=0
keepcache=0
" major major baseurl major major srpms;
(match optional with
| None -> ()
| Some (optionalbaseurl, optionalsrpms) ->
bpf "\
[rhel%d-optional]
name=RHEL %d Server Optional
baseurl=%s
enabled=1
gpgcheck=0
keepcache=0
[rhel%d-optional-source]
name=RHEL %d Server Optional
baseurl=%s
enabled=0
gpgcheck=0
keepcache=0
" major major optionalbaseurl major major optionalsrpms
)
) else (
assert false (* not implemented for RHEL major >= 9 *)
);
Buffer.contents buf
and make_index_fragment os arch index_fragment output nvram revision
expandfs lvexpandfs virtual_size_gb =
let virtual_size = Int64.of_int virtual_size_gb in
let virtual_size = Int64.mul virtual_size 1024_L in
let virtual_size = Int64.mul virtual_size 1024_L in
let virtual_size = Int64.mul virtual_size 1024_L in
let chan = open_out (index_fragment ^ ".new") in
let fpf fs = fprintf chan fs in
fpf "[%s]\n" (string_of_os_noarch os);
fpf "name=%s\n" (long_name_of_os os arch);
fpf "osinfo=%s\n" (os_variant_of_os os arch);
fpf "arch=%s\n" (string_of_arch arch);
fpf "file=%s\n" output;
(match revision with
| None -> ()
| Some i -> fpf "revision=%d\n" i
);
fpf "checksum[sha512]=%s\n" (sha512sum_of_file output);
fpf "format=raw\n";
fpf "size=%Ld\n" virtual_size;
fpf "compressed_size=%d\n" (size_of_file output);
fpf "expand=%s\n" expandfs;
(match lvexpandfs with
| None -> ()
| Some fs -> fpf "lvexpand=%s\n" fs
);
let notes = notes_of_os os arch nvram in
(match notes with
| first :: notes ->
fpf "notes=%s\n" first;
List.iter (fpf " %s\n") notes
| [] -> assert false
);
fpf "\n";
close_out chan;
let cmd =
sprintf "mv %s %s"
(quote (index_fragment ^ ".new")) (quote index_fragment) in
if Sys.command cmd <> 0 then exit 1
and long_name_of_os os arch =
match os, arch with
| CentOS (major, minor), X86_64 ->
sprintf "CentOS %d.%d" major minor
| CentOS (major, minor), arch ->
sprintf "CentOS %d.%d (%s)" major minor (string_of_arch arch)
| Debian (ver, dist), X86_64 ->
sprintf "Debian %d (%s)" ver dist
| Debian (ver, dist), arch ->
sprintf "Debian %d (%s) (%s)" ver dist (string_of_arch arch)
| Fedora ver, X86_64 ->
sprintf "Fedora® %d Server" ver
| Fedora ver, arch ->
sprintf "Fedora® %d Server (%s)" ver (string_of_arch arch)
| RHEL (major, minor), X86_64 ->
sprintf "Red Hat Enterprise Linux® %d.%d" major minor
| RHEL (major, minor), arch ->
sprintf "Red Hat Enterprise Linux® %d.%d (%s)"
major minor (string_of_arch arch)
| Ubuntu (ver, dist), X86_64 ->
sprintf "Ubuntu %s (%s)" ver dist
| Ubuntu (ver, dist), arch ->
sprintf "Ubuntu %s (%s) (%s)" ver dist (string_of_arch arch)
| FreeBSD (major, minor), X86_64 ->
sprintf "FreeBSD %d.%d" major minor
| FreeBSD (major, minor), arch ->
sprintf "FreeBSD %d.%d (%s)" major minor (string_of_arch arch)
| Windows (6, 1, Client), arch ->
sprintf "Windows 7 (%s)" (string_of_arch arch)
| Windows (6, 1, Server), arch ->
sprintf "Windows Server 2008 R2 (%s)" (string_of_arch arch)
| Windows (6, 2, Server), arch ->
sprintf "Windows Server 2012 (%s)" (string_of_arch arch)
| Windows (6, 3, Server), arch ->
sprintf "Windows Server 2012 R2 (%s)" (string_of_arch arch)
| Windows (10, 0, Server), arch ->
sprintf "Windows Server 2016 (%s)" (string_of_arch arch)
| Windows _, _ -> assert false
and notes_of_os os arch nvram =
let args = ref [] in
let add arg = args := arg :: !args in
add (long_name_of_os os arch);
add "";
(match os with
| CentOS _ ->
add "This CentOS image contains only unmodified @Core group packages."
| Debian _ ->
add "This is a minimal Debian install."
| Fedora _ ->
add "This Fedora image contains only unmodified @Core group packages.";
add "";
add "Fedora and the Infinity design logo are trademarks of Red Hat, Inc.";
add "Source and further information is available from http://fedoraproject.org/"
| RHEL _ -> assert false (* cannot happen, see caller *)
| Ubuntu _ ->
add "This is a minimal Ubuntu install."
| FreeBSD _ ->
add "This is an all-default FreeBSD install."
| Windows _ ->
add "This is an unattended Windows install.";
add "";
add "You must have an MSDN subscription to use this image."
);
add "";
(* Specific notes for particular versions. *)
let reconfigure_ssh_host_keys_debian () =
add "This image does not contain SSH host keys. To regenerate them use:";
add "";
add " --firstboot-command \"dpkg-reconfigure openssh-server\"";
add "";
in
let fix_serial_console_debian () =
add "The serial console is not working in this image. To enable it, do:";
add "";
add " --edit '/etc/default/grub:";
add " s/^GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"console=tty0 console=ttyS0,115200n8\"/' \\";
add " --run-command update-grub";
add ""
in
let builder_account_warning () =
add "IMPORTANT WARNING:";
add "It seems to be impossible to create an Ubuntu >= 14.04 image using";
add "preseed without creating a user account. Therefore this image";
add "contains a user account 'builder'. I have disabled it, so that";
add "people who don't read release notes don't get caught out, but you";
add "might still wish to delete it completely.";
add ""
in
(match os with
| CentOS (6, _) ->
add "virt-builder centos-6 will always install the latest 6.x release.";
add ""
| Debian ((8|9), _) ->
reconfigure_ssh_host_keys_debian ();
| Debian _ ->
add "This image is so very minimal that it only includes an ssh server";
reconfigure_ssh_host_keys_debian ();
| Ubuntu ("16.04", _) ->
builder_account_warning ();
fix_serial_console_debian ();
reconfigure_ssh_host_keys_debian ();
| Ubuntu (ver, _) when ver >= "14.04" ->
builder_account_warning ();
reconfigure_ssh_host_keys_debian ();
| Ubuntu _ ->
reconfigure_ssh_host_keys_debian ();
| _ -> ()
);
(match nvram with
| Some vars ->
add "You will need to use the associated UEFI NVRAM variables file:";
add (sprintf " http://libguestfs.org/download/builder/%s" vars);
add "";
| None -> ()
);
add "This template was generated by a script in the libguestfs source tree:";
add " builder/templates/make-template.ml";
add "Associated files used to prepare this template can be found in the";
add "same directory.";
List.rev !args
and read_revision filename =
match (try Some (open_in filename) with Sys_error _ -> None) with
| None -> `No_file
| Some chan ->
let r = ref `No_revision in
let rex = Str.regexp "^revision=\\([0-9]+\\)$" in
(try
let rec loop () =
let line = input_line chan in
if Str.string_match rex line 0 then (
r := `Revision (int_of_string (Str.matched_group 1 line));
raise End_of_file
);
loop ()
in
loop ()
with End_of_file -> ()
);
close_in chan;
!r
and sha512sum_of_file filename =
let cmd = sprintf "sha512sum %s | awk '{print $1}'" (quote filename) in
let chan = Unix.open_process_in cmd in
let line = input_line chan in
let pstat = Unix.close_process_in chan in
check_process_status_for_errors pstat;
line
and size_of_file filename = (Unix.stat filename).Unix.st_size
and open_guest ?(mount = false) filename =
let g = new Guestfs.guestfs () in
g#add_drive_opts ~format:"raw" filename;
g#launch ();
let roots = g#inspect_os () in
if Array.length roots = 0 then (
eprintf "%s: cannot inspect this guest - this may mean guest installation failed\n" prog;
exit 1
);
if mount then (
let root = roots.(0) in
let mps = g#inspect_get_mountpoints root in
let cmp (a,_) (b,_) = compare (String.length a) (String.length b) in
let mps = List.sort cmp mps in
List.iter (fun (mp, dev) -> g#mount dev mp) mps
);
g
and check_process_status_for_errors = function
| Unix.WEXITED 0 -> ()
| Unix.WEXITED i ->
eprintf "command exited with %d\n%!" i;
exit 1
| Unix.WSIGNALED i ->
eprintf "command killed by signal %d\n%!" i;
exit 1
| Unix.WSTOPPED i ->
eprintf "command stopped by signal %d\n%!" i;
exit 1
and random8 =
let chars = "abcdefghijklmnopqrstuvwxyz0123456789" in
fun () ->
String.concat "" (
List.map (
fun _ ->
let c = Random.int 36 in
let c = chars.[c] in
String.make 1 c
) [1;2;3;4;5;6;7;8]
)
let () = main ()