diff --git a/builder/templates/make-template.ml b/builder/templates/make-template.ml index cf184a65f..58798d2fa 100755 --- a/builder/templates/make-template.ml +++ b/builder/templates/make-template.ml @@ -32,6 +32,8 @@ open Printf +let windows_installers = "/mnt/media/installers/Windows" + let prog = "make-template" (* Ensure that a file is deleted on exit. *) @@ -80,6 +82,8 @@ type os = | 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 = @@ -102,6 +106,7 @@ let rec main () = (* 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 @@ -350,6 +355,7 @@ and os_of_string os ver = | "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 @@ -364,6 +370,18 @@ and parse_major_minor 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 @@ -416,6 +434,14 @@ and filename_of_os os 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 "" @@ -428,16 +454,18 @@ and string_of_os_noarch = function | 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 _ -> false + | FreeBSD _ | Windows _ -> false and is_selinux_os = function | RHEL _ | CentOS _ | Fedora _ -> true | Debian _ | Ubuntu _ - | FreeBSD _ -> false + | FreeBSD _ | Windows _ -> false and needs_uefi os arch = match os, arch with @@ -445,9 +473,17 @@ and needs_uefi os arch = | RHEL _, Aarch64 -> true | RHEL _, _ | CentOS _, _ | Fedora _, _ | Debian _, _ | Ubuntu _, _ - | FreeBSD _, _ -> false + | FreeBSD _, _ | Windows _, _ -> false -and get_virtual_size_gb os arch = 6 +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 @@ -463,6 +499,9 @@ and make_kickstart os arch = (* 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 @@ -640,6 +679,138 @@ and copy_preseed_to_temporary source = 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 " + + + + + true + + %s + OnError + + + + + + 0 + true + + + + 1 + Primary + 300 + + + + 2 + Primary + true + + + + + + 1 + 1 + + NTFS + true + + + + 2 + 2 + + C + NTFS + + + + OnError + + + + + Never + + + /IMAGE/INDEX + 1 + + + + 0 + 2 + + + + + + + + en-US + + en-US + en-US + en-US + + +" + 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 -> @@ -753,6 +924,25 @@ and make_boot_media os arch = 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 @@ -776,7 +966,7 @@ The FreeBSD install is not automated. Select all defaults, except: (* If the install is not automated and we need a graphical console. *) and needs_graphics = function | CentOS _ | RHEL _ | Debian _ | Ubuntu _ | Fedora _ -> false - | FreeBSD _ -> true + | FreeBSD _ | Windows _ -> true (* NB: Arguments do not need to be quoted, because we pass them * directly to exec(2). @@ -789,9 +979,23 @@ and make_virt_install_command os arch ks tmpname tmpout tmpefivars add "virt-install"; (* This ensures the libvirt domain will be automatically deleted - * when virt-install exits. + * when virt-install exits. However it doesn't work for certain + * types of guest. *) - add "--transient"; + (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); @@ -845,7 +1049,7 @@ and make_virt_install_command os arch ks tmpname tmpout tmpefivars | Debian _ | Ubuntu _ -> "auto" | Fedora _ | RHEL _ | CentOS _ -> sprintf "ks=file:/%s" (Filename.basename ks) - | FreeBSD _ -> assert false in + | FreeBSD _ | Windows _ -> assert false in let proxy = let p = try Some (Sys.getenv "http_proxy") with Not_found -> None in match p with @@ -853,19 +1057,19 @@ and make_virt_install_command os arch ks tmpname tmpout tmpefivars (match os with | Fedora _ | RHEL _ | CentOS _ | Ubuntu _ -> "" | Debian _ -> "mirror/http/proxy=" - | FreeBSD _ -> assert false + | FreeBSD _ | Windows _ -> assert false ) | Some p -> match os with | Fedora _ | RHEL _ | CentOS _ -> "proxy=" ^ p | Debian _ | Ubuntu _ -> "mirror/http/proxy=" ^ p - | FreeBSD _ -> assert false in + | 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 _ -> () + | FreeBSD _ | Windows _ -> () ); add (sprintf "--disk=%s,size=%d,format=raw" @@ -873,7 +1077,19 @@ and make_virt_install_command os arch ks tmpname tmpout tmpefivars (match boot_media with | Location location -> add (sprintf "--location=%s" location) - | CDRom iso -> add (sprintf "--cdrom=%s" iso) + | 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"; @@ -907,6 +1123,13 @@ and os_variant_of_os ?(for_fedora = false) os arch = | 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 @@ -935,6 +1158,13 @@ and os_variant_of_os ?(for_fedora = false) os arch = | 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 = @@ -952,7 +1182,8 @@ and kernel_cmdline_of_os os arch = | (RHEL _|CentOS _), PPC64 | (RHEL _|CentOS _), PPC64le -> "console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH" - | FreeBSD _, (PPC64|PPC64le) -> assert false + + | FreeBSD _, _ | Windows _, _ -> assert false and make_postinstall os arch = match os with @@ -974,7 +1205,7 @@ and make_postinstall os arch = g#write "/etc/yum.repos.d/download.devel.redhat.com.repo" yum_conf ) - | RHEL _ | Fedora _ | CentOS _ | FreeBSD _ -> None + | RHEL _ | Fedora _ | CentOS _ | FreeBSD _ | Windows _ -> None and make_rhel_yum_conf major minor arch = let buf = Buffer.create 4096 in @@ -1156,6 +1387,18 @@ and long_name_of_os os arch = | 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 @@ -1178,6 +1421,10 @@ and notes_of_os os arch nvram = 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 "";