Pino Toscano
2016-Aug-25 16:05 UTC
[Libguestfs] [PATCH v2] v2v: factor out bootloader handling
Create an object hierarchy to represent different bootloaders for Linux guests, moving the separate handling of grub1 and grub2 in different classes: this isolates the code for each type of bootloader together, instead of scattering it all around. This is mostly code refactoring, with no actual behaviour change. --- po/POTFILES-ml | 1 + v2v/Makefile.am | 2 + v2v/convert_linux.ml | 294 ++--------------------------------------- v2v/linux_bootloaders.ml | 325 ++++++++++++++++++++++++++++++++++++++++++++++ v2v/linux_bootloaders.mli | 44 +++++++ 5 files changed, 385 insertions(+), 281 deletions(-) create mode 100644 v2v/linux_bootloaders.ml create mode 100644 v2v/linux_bootloaders.mli diff --git a/po/POTFILES-ml b/po/POTFILES-ml index b7b39fa..6a22f2c 100644 --- a/po/POTFILES-ml +++ b/po/POTFILES-ml @@ -120,6 +120,7 @@ v2v/input_libvirtxml.ml v2v/input_ova.ml v2v/inspect_source.ml v2v/linux.ml +v2v/linux_bootloaders.ml v2v/modules_list.ml v2v/output_glance.ml v2v/output_libvirt.ml diff --git a/v2v/Makefile.am b/v2v/Makefile.am index d53e6e6..8a59ab5 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -42,6 +42,7 @@ SOURCES_MLI = \ input_ova.mli \ inspect_source.mli \ linux.mli \ + linux_bootloaders.mli \ modules_list.mli \ output_glance.mli \ output_libvirt.mli \ @@ -81,6 +82,7 @@ SOURCES_ML = \ input_libvirt_xen_ssh.ml \ input_libvirt.ml \ input_ova.ml \ + linux_bootloaders.ml \ convert_linux.ml \ convert_windows.ml \ output_null.ml \ diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml index 2a53315..7829612 100644 --- a/v2v/convert_linux.ml +++ b/v2v/convert_linux.ml @@ -87,45 +87,9 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps (* Clean RPM database. This must be done early to avoid RHBZ#1143866. *) Array.iter g#rm_f (g#glob_expand "/var/lib/rpm/__db.00?"); - (* What grub is installed? *) - let grub_config, grub - let locations = [ - "/boot/grub2/grub.cfg", `Grub2; - "/boot/grub/menu.lst", `Grub1; - "/boot/grub/grub.conf", `Grub1; - ] in - let locations - match inspect.i_firmware with - | I_UEFI _ -> - [ - "/boot/efi/EFI/redhat/grub.cfg", `Grub2; - "/boot/efi/EFI/redhat/grub.conf", `Grub1; - ] @ locations - | I_BIOS -> locations in - try - List.find ( - fun (grub_config, _) -> g#is_file ~followsymlinks:true grub_config - ) locations - with - Not_found -> - error (f_"no grub1/grub-legacy or grub2 configuration file was found") in - - (* Grub prefix? Usually "/boot". *) - let grub_prefix - match grub with - | `Grub2 -> "" - | `Grub1 -> - if grub_config = "/boot/efi/EFI/redhat/grub.conf" then ( - g#aug_transform "grub" "/boot/efi/EFI/redhat/grub.conf"; - Linux.augeas_reload g; - ); - - let mounts = g#inspect_get_mountpoints inspect.i_root in - try - List.find ( - fun path -> List.mem_assoc path mounts - ) [ "/boot/grub"; "/boot" ] - with Not_found -> "" in + (* Detect the installed bootloader. *) + let bootloader = Linux_bootloaders.detect_bootloader g inspect in + Linux.augeas_reload g; (* What kernel/kernel-like packages are installed on the current guest? *) let installed_kernels : kernel_info list @@ -276,88 +240,7 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps * list is the default booting kernel. *) let grub_kernels : kernel_info list - (* Helper function for SUSE: remove (hdX,X) prefix from a path. *) - let remove_hd_prefix - let rex = Str.regexp "^(hd.*)\\(.*\\)" in - Str.replace_first rex "\\1" - in - - let vmlinuzes - match grub with - | `Grub1 -> - let paths - let expr = sprintf "/files%s/title/kernel" grub_config in - let paths = g#aug_match expr in - let paths = Array.to_list paths in - - (* Remove duplicates. *) - let paths = remove_duplicates paths in - - (* Get the default kernel from grub if it's set. *) - let default - let expr = sprintf "/files%s/default" grub_config in - try - let idx = g#aug_get expr in - let idx = int_of_string idx in - (* Grub indices are zero-based, augeas is 1-based. *) - let expr - sprintf "/files%s/title[%d]/kernel" grub_config (idx+1) in - Some expr - with G.Error msg - when String.find msg "aug_get: no matching node" >= 0 -> - None in - - (* If a default kernel was set, put it at the beginning of the paths - * list. If not set, assume the first kernel always boots (?) - *) - match default with - | None -> paths - | Some p -> p :: List.filter ((<>) p) paths in - - (* Resolve the Augeas paths to kernel filenames. *) - let vmlinuzes = List.map g#aug_get paths in - - (* Make sure kernel does not begin with (hdX,X). *) - let vmlinuzes = List.map remove_hd_prefix vmlinuzes in - - (* Prepend grub filesystem. *) - List.map ((^) grub_prefix) vmlinuzes - - | `Grub2 -> - let get_default_image () - let cmd - if g#exists "/sbin/grubby" then - [| "grubby"; "--default-kernel" |] - else - [| "/usr/bin/perl"; "-MBootloader::Tools"; "-e"; " - InitLibrary(); - my $default = Bootloader::Tools::GetDefaultSection(); - print $default->{image}; - " |] in - match g#command cmd with - | "" -> None - | k -> - let len = String.length k in - let k - if len > 0 && k.[len-1] = '\n' then - String.sub k 0 (len-1) - else k in - Some (remove_hd_prefix k) - in - - let vmlinuzes - (match get_default_image () with - | None -> [] - | Some k -> [k]) @ - (* This is how the grub2 config generator enumerates kernels. *) - Array.to_list (g#glob_expand "/boot/kernel-*") @ - Array.to_list (g#glob_expand "/boot/vmlinuz-*") @ - Array.to_list (g#glob_expand "/vmlinuz-*") in - let rex = Str.regexp ".*\\.\\(dpkg-.*|rpmsave|rpmnew\\)$" in - let vmlinuzes = List.filter ( - fun file -> not (Str.string_match rex file 0) - ) vmlinuzes in - vmlinuzes in + let vmlinuzes = bootloader#list_kernels () in (* Map these to installed kernels. *) filter_map ( @@ -396,21 +279,8 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps (* Conversion step. *) let rec augeas_grub_configuration () - match grub with - | `Grub1 -> - (* Ensure Augeas is reading the grub configuration file, and if not - * then add it. - *) - let incls = g#aug_match "/augeas/load/Grub/incl" in - let incls = Array.to_list incls in - let incls_contains_conf - List.exists (fun incl -> g#aug_get incl = grub_config) incls in - if not incls_contains_conf then ( - g#aug_set "/augeas/load/Grub/incl[last()+1]" grub_config; - Linux.augeas_reload g; - ) - - | `Grub2 -> () (* Not necessary for grub2. *) + if bootloader#set_augeas_configuration () then + Linux.augeas_reload g and unconfigure_xen () (* Remove kmod-xenpv-* (RHEL 3). *) @@ -740,7 +610,7 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps let kernels = List.rev kernels (* so best is first *) in List.hd kernels in if best_kernel <> List.hd grub_kernels then - grub_set_bootable best_kernel; + bootloader#set_default_kernel best_kernel.ki_vmlinuz; (* Does the best/bootable kernel support virtio? *) let virtio = best_kernel.ki_supports_virtio in @@ -758,46 +628,6 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps best_kernel, virtio - and grub_set_bootable kernel - match grub with - | `Grub1 -> - if not (String.is_prefix kernel.ki_vmlinuz grub_prefix) then - error (f_"kernel %s is not under grub tree %s") - kernel.ki_vmlinuz grub_prefix; - let kernel_under_grub_prefix - let prefix_len = String.length grub_prefix in - let kernel_len = String.length kernel.ki_vmlinuz in - String.sub kernel.ki_vmlinuz prefix_len (kernel_len - prefix_len) in - - (* Find the grub entry for the given kernel. *) - let paths = g#aug_match (sprintf "/files%s/title/kernel[. = '%s']" - grub_config kernel_under_grub_prefix) in - let paths = Array.to_list paths in - if paths = [] then - error (f_"didn't find grub entry for kernel %s") kernel.ki_vmlinuz; - let path = List.hd paths in - let rex = Str.regexp ".*/title\\[\\([1-9][0-9]*\\)\\]/kernel" in - if not (Str.string_match rex path 0) then - error (f_"internal error: regular expression did not match '%s'") - path; - let index = int_of_string (Str.matched_group 1 path) - 1 in - g#aug_set (sprintf "/files%s/default" grub_config) (string_of_int index); - g#aug_save () - - | `Grub2 -> - let cmd - if g#exists "/sbin/grubby" then - [| "grubby"; "--set-default"; kernel.ki_vmlinuz |] - else - [| "/usr/bin/perl"; "-MBootloader::Tools"; "-e"; sprintf " - InitLibrary(); - my @sections = GetSectionList(type=>image, image=>\"%s\"); - my $section = GetSection(@sections); - my $newdefault = $section->{name}; - SetGlobals(default, \"$newdefault\"); - " kernel.ki_vmlinuz |] in - ignore (g#command cmd) - (* Even though the kernel was already installed (this version of * virt-v2v does not install new kernels), it could have an * initrd that does not have support virtio. Therefore rebuild @@ -963,28 +793,6 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps g#aug_save () - and grub_configure_console () - match grub with - | `Grub1 -> - let rex = Str.regexp "\\(.*\\)\\b\\([xh]vc0\\)\\b\\(.*\\)" in - let expr = sprintf "/files%s/title/kernel/console" grub_config in - - let paths = g#aug_match expr in - let paths = Array.to_list paths in - List.iter ( - fun path -> - let console = g#aug_get path in - if Str.string_match rex console 0 then ( - let console = Str.global_replace rex "\\1ttyS0\\3" console in - g#aug_set path console - ) - ) paths; - - g#aug_save () - - | `Grub2 -> - grub2_update_console ~remove:false - (* If the target doesn't support a serial console, we want to remove * all references to it instead. *) @@ -1013,71 +821,6 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps g#aug_save () - and grub_remove_console () - match grub with - | `Grub1 -> - let rex = Str.regexp "\\(.*\\)\\b\\([xh]vc0\\)\\b\\(.*\\)" in - let expr = sprintf "/files%s/title/kernel/console" grub_config in - - let rec loop = function - | [] -> () - | path :: paths -> - let console = g#aug_get path in - if Str.string_match rex console 0 then ( - ignore (g#aug_rm path); - (* All the paths are invalid, restart the loop. *) - let paths = g#aug_match expr in - let paths = Array.to_list paths in - loop paths - ) - else - loop paths - in - let paths = g#aug_match expr in - let paths = Array.to_list paths in - loop paths; - - g#aug_save () - - | `Grub2 -> - grub2_update_console ~remove:true - - and grub2_update_console ~remove - let rex = Str.regexp "\\(.*\\)\\bconsole=[xh]vc0\\b\\(.*\\)" in - - let paths = [ - "/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX"; - "/files/etc/default/grub/GRUB_CMDLINE_LINUX"; - "/files/etc/default/grub/GRUB_CMDLINE_LINUX_DEFAULT" - ] in - let paths = List.map g#aug_match paths in - let paths = List.map Array.to_list paths in - let paths = List.flatten paths in - match paths with - | [] -> - if not remove then - warning (f_"could not add grub2 serial console (ignored)") - else - warning (f_"could not remove grub2 serial console (ignored)") - | path :: _ -> - let grub_cmdline = g#aug_get path in - if Str.string_match rex grub_cmdline 0 then ( - let new_grub_cmdline - if not remove then - Str.global_replace rex "\\1console=ttyS0\\2" grub_cmdline - else - Str.global_replace rex "\\1\\2" grub_cmdline in - g#aug_set path new_grub_cmdline; - g#aug_save (); - - try - ignore (g#command [| "grub2-mkconfig"; "-o"; grub_config |]) - with - G.Error msg -> - warning (f_"could not rebuild grub2 configuration file (%s). This may mean that grub output will not be sent to the serial port, but otherwise should be harmless. Original error message: %s") - grub_config msg - ) - and supports_acpi () (* ACPI known to cause RHEL 3 to fail. *) if family = `RHEL_family && inspect.i_major_version == 3 then @@ -1303,19 +1046,9 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps let paths = [ (* /etc/fstab *) "/files/etc/fstab/*/spec"; - - (* grub-legacy config *) - "/files" ^ grub_config ^ "/*/kernel/root"; - "/files" ^ grub_config ^ "/*/kernel/resume"; - "/files/boot/grub/device.map/*[label() != \"#comment\"]"; - "/files/etc/sysconfig/grub/boot"; - - (* grub2 config *) - "/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX"; - "/files/etc/default/grub/GRUB_CMDLINE_LINUX"; - "/files/etc/default/grub/GRUB_CMDLINE_LINUX_DEFAULT"; - "/files/boot/grub2/device.map/*[label() != \"#comment\"]"; ] in + (* Bootloader config *) + let paths = paths @ bootloader#augeas_device_patterns in (* Which of these paths actually exist? *) let paths @@ -1393,9 +1126,8 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps if !changed then ( g#aug_save (); - (* If it's grub2, we have to regenerate the config files. *) - if grub = `Grub2 then - ignore (g#command [| "grub2-mkconfig"; "-o"; grub_config |]); + (* Make sure the bootloader is up-to-date. *) + bootloader#update (); Linux.augeas_reload g ); @@ -1425,10 +1157,10 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps if keep_serial_console then ( configure_console (); - grub_configure_console (); + bootloader#configure_console (); ) else ( remove_console (); - grub_remove_console (); + bootloader#remove_console (); ); let acpi = supports_acpi () in diff --git a/v2v/linux_bootloaders.ml b/v2v/linux_bootloaders.ml new file mode 100644 index 0000000..f6cbd5a --- /dev/null +++ b/v2v/linux_bootloaders.ml @@ -0,0 +1,325 @@ +(* virt-v2v + * Copyright (C) 2009-2016 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 Printf + +open Common_gettext.Gettext +open Common_utils + +open Types +open Utils + +module G = Guestfs + +class virtual bootloader = object + method virtual name : string + method virtual augeas_device_patterns : string list + method virtual list_kernels : unit -> string list + method virtual set_default_kernel : string -> unit + method set_augeas_configuration () = false + method virtual configure_console : unit -> unit + method virtual remove_console : unit -> unit + method update () = () +end + +(* Helper type used in detect_bootloader. *) +type bootloader_type + | Grub1 + | Grub2 + +(* Helper function for SUSE: remove (hdX,X) prefix from a path. *) +let remove_hd_prefix path + let rex = Str.regexp "^(hd.*)\\(.*\\)" in + Str.replace_first rex "\\1" path + +(* Grub1 (AKA grub-legacy) representation. *) +class bootloader_grub1 (g : G.guestfs) inspect grub_config + (* Grub prefix? Usually "/boot". *) + let grub_prefix + let mounts = g#inspect_get_mountpoints inspect.i_root in + try + List.find ( + fun path -> List.mem_assoc path mounts + ) [ "/boot/grub"; "/boot" ] + with Not_found -> "" in +object + inherit bootloader + + method name = "grub1" + + method augeas_device_patterns = [ + "/files" ^ grub_config ^ "/*/kernel/root"; + "/files" ^ grub_config ^ "/*/kernel/resume"; + "/files/boot/grub/device.map/*[label() != \"#comment\"]"; + "/files/etc/sysconfig/grub/boot"; + ] + + method list_kernels () + let paths + let expr = sprintf "/files%s/title/kernel" grub_config in + let paths = g#aug_match expr in + let paths = Array.to_list paths in + + (* Remove duplicates. *) + let paths = remove_duplicates paths in + + (* Get the default kernel from grub if it's set. *) + let default + let expr = sprintf "/files%s/default" grub_config in + try + let idx = g#aug_get expr in + let idx = int_of_string idx in + (* Grub indices are zero-based, augeas is 1-based. *) + let expr + sprintf "/files%s/title[%d]/kernel" grub_config (idx+1) in + Some expr + with G.Error msg + when String.find msg "aug_get: no matching node" >= 0 -> + None in + + (* If a default kernel was set, put it at the beginning of the paths + * list. If not set, assume the first kernel always boots (?) + *) + match default with + | None -> paths + | Some p -> p :: List.filter ((<>) p) paths in + + (* Resolve the Augeas paths to kernel filenames. *) + let vmlinuzes = List.map g#aug_get paths in + + (* Make sure kernel does not begin with (hdX,X). *) + let vmlinuzes = List.map remove_hd_prefix vmlinuzes in + + (* Prepend grub filesystem. *) + List.map ((^) grub_prefix) vmlinuzes + + method set_default_kernel vmlinuz + if not (String.is_prefix vmlinuz grub_prefix) then + error (f_"kernel %s is not under grub tree %s") + vmlinuz grub_prefix; + let kernel_under_grub_prefix + let prefix_len = String.length grub_prefix in + let kernel_len = String.length vmlinuz in + String.sub vmlinuz prefix_len (kernel_len - prefix_len) in + + (* Find the grub entry for the given kernel. *) + let paths = g#aug_match (sprintf "/files%s/title/kernel[. = '%s']" + grub_config kernel_under_grub_prefix) in + let paths = Array.to_list paths in + if paths = [] then + error (f_"didn't find grub entry for kernel %s") vmlinuz; + let path = List.hd paths in + let rex = Str.regexp ".*/title\\[\\([1-9][0-9]*\\)\\]/kernel" in + if not (Str.string_match rex path 0) then + error (f_"internal error: regular expression did not match '%s'") + path; + let index = int_of_string (Str.matched_group 1 path) - 1 in + g#aug_set (sprintf "/files%s/default" grub_config) (string_of_int index); + g#aug_save () + + method set_augeas_configuration () + let incls = g#aug_match "/augeas/load/Grub/incl" in + let incls = Array.to_list incls in + let incls_contains_conf + List.exists (fun incl -> g#aug_get incl = grub_config) incls in + if not incls_contains_conf then ( + g#aug_set "/augeas/load/Grub/incl[last()+1]" grub_config; + true; + ) else false + + method configure_console () + let rex = Str.regexp "\\(.*\\)\\b\\([xh]vc0\\)\\b\\(.*\\)" in + let expr = sprintf "/files%s/title/kernel/console" grub_config in + + let paths = g#aug_match expr in + let paths = Array.to_list paths in + List.iter ( + fun path -> + let console = g#aug_get path in + if Str.string_match rex console 0 then ( + let console = Str.global_replace rex "\\1ttyS0\\3" console in + g#aug_set path console + ) + ) paths; + + g#aug_save () + + method remove_console () + let rex = Str.regexp "\\(.*\\)\\b\\([xh]vc0\\)\\b\\(.*\\)" in + let expr = sprintf "/files%s/title/kernel/console" grub_config in + + let rec loop = function + | [] -> () + | path :: paths -> + let console = g#aug_get path in + if Str.string_match rex console 0 then ( + ignore (g#aug_rm path); + (* All the paths are invalid, restart the loop. *) + let paths = g#aug_match expr in + let paths = Array.to_list paths in + loop paths + ) + else + loop paths + in + let paths = g#aug_match expr in + let paths = Array.to_list paths in + loop paths; + + g#aug_save () +end + +(* Grub2 representation. *) +class bootloader_grub2 (g : G.guestfs) grub_config + let grub2_update_console ~remove + let rex = Str.regexp "\\(.*\\)\\bconsole=[xh]vc0\\b\\(.*\\)" in + + let paths = [ + "/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX"; + "/files/etc/default/grub/GRUB_CMDLINE_LINUX"; + "/files/etc/default/grub/GRUB_CMDLINE_LINUX_DEFAULT" + ] in + let paths = List.map g#aug_match paths in + let paths = List.map Array.to_list paths in + let paths = List.flatten paths in + match paths with + | [] -> + if not remove then + warning (f_"could not add grub2 serial console (ignored)") + else + warning (f_"could not remove grub2 serial console (ignored)") + | path :: _ -> + let grub_cmdline = g#aug_get path in + if Str.string_match rex grub_cmdline 0 then ( + let new_grub_cmdline + if not remove then + Str.global_replace rex "\\1console=ttyS0\\2" grub_cmdline + else + Str.global_replace rex "\\1\\2" grub_cmdline in + g#aug_set path new_grub_cmdline; + g#aug_save (); + + try + ignore (g#command [| "grub2-mkconfig"; "-o"; grub_config |]) + with + G.Error msg -> + warning (f_"could not rebuild grub2 configuration file (%s). This may mean that grub output will not be sent to the serial port, but otherwise should be harmless. Original error message: %s") + grub_config msg + ) in +object + inherit bootloader + + method name = "grub2" + + method augeas_device_patterns = [ + "/files/etc/sysconfig/grub/GRUB_CMDLINE_LINUX"; + "/files/etc/default/grub/GRUB_CMDLINE_LINUX"; + "/files/etc/default/grub/GRUB_CMDLINE_LINUX_DEFAULT"; + "/files/boot/grub2/device.map/*[label() != \"#comment\"]"; + ] + + method list_kernels () + let get_default_image () + let cmd + if g#exists "/sbin/grubby" then + [| "grubby"; "--default-kernel" |] + else + [| "/usr/bin/perl"; "-MBootloader::Tools"; "-e"; " + InitLibrary(); + my $default = Bootloader::Tools::GetDefaultSection(); + print $default->{image}; + " |] in + match g#command cmd with + | "" -> None + | k -> + let len = String.length k in + let k + if len > 0 && k.[len-1] = '\n' then + String.sub k 0 (len-1) + else k in + Some (remove_hd_prefix k) + in + + let vmlinuzes + (match get_default_image () with + | None -> [] + | Some k -> [k]) @ + (* This is how the grub2 config generator enumerates kernels. *) + Array.to_list (g#glob_expand "/boot/kernel-*") @ + Array.to_list (g#glob_expand "/boot/vmlinuz-*") @ + Array.to_list (g#glob_expand "/vmlinuz-*") in + let rex = Str.regexp ".*\\.\\(dpkg-.*|rpmsave|rpmnew\\)$" in + let vmlinuzes = List.filter ( + fun file -> not (Str.string_match rex file 0) + ) vmlinuzes in + vmlinuzes + + method set_default_kernel vmlinuz + let cmd + if g#exists "/sbin/grubby" then + [| "grubby"; "--set-default"; vmlinuz |] + else + [| "/usr/bin/perl"; "-MBootloader::Tools"; "-e"; sprintf " + InitLibrary(); + my @sections = GetSectionList(type=>image, image=>\"%s\"); + my $section = GetSection(@sections); + my $newdefault = $section->{name}; + SetGlobals(default, \"$newdefault\"); + " vmlinuz |] in + ignore (g#command cmd) + + method configure_console () + grub2_update_console ~remove:false + + method remove_console () + grub2_update_console ~remove:true + + method update () + ignore (g#command [| "grub2-mkconfig"; "-o"; grub_config |]) +end + +let detect_bootloader (g : G.guestfs) inspect + let config_file, typ + let locations = [ + "/boot/grub2/grub.cfg", Grub2; + "/boot/grub/menu.lst", Grub1; + "/boot/grub/grub.conf", Grub1; + ] in + let locations + match inspect.i_firmware with + | I_UEFI _ -> + [ + "/boot/efi/EFI/redhat/grub.cfg", Grub2; + "/boot/efi/EFI/redhat/grub.conf", Grub1; + ] @ locations + | I_BIOS -> locations in + try + List.find ( + fun (config_file, _) -> g#is_file ~followsymlinks:true config_file + ) locations + with + Not_found -> + error (f_"no bootloader detected") in + + match typ with + | Grub1 -> + if config_file = "/boot/efi/EFI/redhat/grub.conf" then + g#aug_transform "grub" "/boot/efi/EFI/redhat/grub.conf"; + + new bootloader_grub1 g inspect config_file + | Grub2 -> new bootloader_grub2 g config_file diff --git a/v2v/linux_bootloaders.mli b/v2v/linux_bootloaders.mli new file mode 100644 index 0000000..cf115e3 --- /dev/null +++ b/v2v/linux_bootloaders.mli @@ -0,0 +1,44 @@ +(* virt-v2v + * Copyright (C) 2009-2016 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. + *) + +class virtual bootloader : object + method virtual name : string + (** The name of the bootloader. *) + method virtual augeas_device_patterns : string list + (** A list of Augeas patterns to search for device names. *) + method virtual list_kernels : unit -> string list + (** Lists all the kernels configured in the bootloader. *) + method virtual set_default_kernel : string -> unit + (** Sets the specified vmlinuz path as default bootloader entry. *) + method set_augeas_configuration : unit -> bool + (** Checks whether Augeas is reading the configuration file + of the bootloader, and if not then add it. + + Returns whether Augeas needs to be reloaded. *) + method virtual configure_console : unit -> unit + (** Sets up the console for the available kernels. *) + method virtual remove_console : unit -> unit + (** Removes the console in all the available kernels. *) + method update : unit -> unit + (** Update the bootloader. *) +end +(** Encapsulates a Linux boot loader as object. *) + +val detect_bootloader : Guestfs.guestfs -> Types.inspect -> bootloader +(** Detects the bootloader on the guest, and creates the object + representing it. *) -- 2.7.4
Richard W.M. Jones
2016-Aug-25 16:36 UTC
Re: [Libguestfs] [PATCH v2] v2v: factor out bootloader handling
On Thu, Aug 25, 2016 at 06:05:16PM +0200, Pino Toscano wrote:> +(* Grub2 representation. *) > +class bootloader_grub2 (g : G.guestfs) grub_config > + let grub2_update_console ~removeI checked the before and after code and I don't think anything has been missed out. My only comment is it seems a bit awkward stuffing the grub2_update_console function definition into what is effectively the class constructor. Maybe it should be hoisted to the top level of the Linux_bootloaders module (not exported, of course), or turned into a private method of the class? Anyway as this is just a quibble, ACK. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://people.redhat.com/~rjones/virt-top
Pino Toscano
2016-Aug-26 08:21 UTC
Re: [Libguestfs] [PATCH v2] v2v: factor out bootloader handling
On Thursday, 25 August 2016 17:36:14 CEST Richard W.M. Jones wrote:> On Thu, Aug 25, 2016 at 06:05:16PM +0200, Pino Toscano wrote: > > +(* Grub2 representation. *) > > +class bootloader_grub2 (g : G.guestfs) grub_config > > + let grub2_update_console ~remove > > I checked the before and after code and I don't think anything has > been missed out. > > My only comment is it seems a bit awkward stuffing the > grub2_update_console function definition into what is effectively the > class constructor. Maybe it should be hoisted to the top level of the > Linux_bootloaders module (not exported, of course), or turned into a > private method of the class?Indeed -- I turned that into a proper private class method, so it is not available outside the bootloader_grub2 class.> Anyway as this is just a quibble, ACK.Quibbling is fine :) Pushed with the aforementioned change. Thanks for the thorough review, -- Pino Toscano
Possibly Parallel Threads
- Re: [PATCH v2] v2v: factor out bootloader handling
- [PATCH] v2v: factor out bootloader handling
- [PATCH v2] v2v: factor out bootloader handling
- [PATCH v5 1/3] v2v: bootloaders: search grub config for all distributions
- Re: [PATCH v6 1/1] v2v: bootloaders: search grub config for all distributions