[Libguestfs] [PATCH] v2v: factor out bootloader handling

Pino Toscano ptoscano at redhat.com
Fri Nov 20 14:06:45 UTC 2015


Create an object hierarchy to represent different bootloaders for Linux
guests, moving the separate handling of grub1 and grub2 in different
classes.  This will allow us to support more bootloaders in the future.

This is mostly code refactoring, with no actual behaviour change.
---
 po/POTFILES-ml       |   1 +
 v2v/Makefile.am      |   2 +
 v2v/bootloaders.ml   | 317 +++++++++++++++++++++++++++++++++++++++++++++++++++
 v2v/bootloaders.mli  |  44 +++++++
 v2v/convert_linux.ml | 274 ++------------------------------------------
 5 files changed, 375 insertions(+), 263 deletions(-)
 create mode 100644 v2v/bootloaders.ml
 create mode 100644 v2v/bootloaders.mli

diff --git a/po/POTFILES-ml b/po/POTFILES-ml
index 00a9d63..a9b0b59 100644
--- a/po/POTFILES-ml
+++ b/po/POTFILES-ml
@@ -97,6 +97,7 @@ sysprep/sysprep_operation_utmp.ml
 sysprep/sysprep_operation_yum_uuid.ml
 v2v/DOM.ml
 v2v/OVF.ml
+v2v/bootloaders.ml
 v2v/changeuid.ml
 v2v/cmdline.ml
 v2v/convert_linux.ml
diff --git a/v2v/Makefile.am b/v2v/Makefile.am
index f2919b7..db5aecd 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -42,6 +42,7 @@ EXTRA_DIST = \
 CLEANFILES = *~ *.annot *.cmi *.cmo *.cmx *.cmxa *.o virt-v2v
 
 SOURCES_MLI = \
+	bootloaders.mli \
 	cmdline.mli \
 	convert_linux.mli \
 	convert_windows.mli \
@@ -94,6 +95,7 @@ SOURCES_ML = \
 	input_libvirt_xen_ssh.ml \
 	input_libvirt.ml \
 	input_ova.ml \
+	bootloaders.ml \
 	convert_linux.ml \
 	convert_windows.ml \
 	output_null.ml \
diff --git a/v2v/bootloaders.ml b/v2v/bootloaders.ml
new file mode 100644
index 0000000..2c5f421
--- /dev/null
+++ b/v2v/bootloaders.ml
@@ -0,0 +1,317 @@
+(* virt-v2v
+ * Copyright (C) 2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open 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 : 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 inspect grub_config =
+  let 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 ((^) prefix) vmlinuzes
+
+  method set_default kernel =
+    if not (String.is_prefix kernel prefix) then
+      error (f_"kernel %s is not under grub tree %s")
+        kernel prefix;
+    let kernel_under_grub_prefix =
+      let prefix_len = String.length prefix in
+      let kernel_len = String.length kernel in
+      String.sub kernel 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;
+    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 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\\3" grub_cmdline
+          else
+            Str.global_replace rex "\\1\\3" 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 =
+    let cmd =
+      if g#exists "/sbin/grubby" then
+        [| "grubby"; "--set-default"; kernel |]
+      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 |] 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 : Guestfs.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 =
+      if inspect.i_uefi then
+        ("/boot/efi/EFI/redhat/grub.cfg", Grub2) :: locations
+      else
+        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 -> new bootloader_grub1 g inspect config_file
+  | Grub2 -> new bootloader_grub2 g config_file
diff --git a/v2v/bootloaders.mli b/v2v/bootloaders.mli
new file mode 100644
index 0000000..b776a51
--- /dev/null
+++ b/v2v/bootloaders.mli
@@ -0,0 +1,44 @@
+(* virt-v2v
+ * Copyright (C) 2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+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 : 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 UNIX boot loader as object. *)
+
+val detect_bootloader : Guestfs.guestfs -> Types.inspect -> bootloader
+(** Detects the bootloader on the guest, and creates the object
+    representing it. *)
diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml
index 14f232f..81dcc14 100644
--- a/v2v/convert_linux.ml
+++ b/v2v/convert_linux.ml
@@ -89,37 +89,8 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source =
   let dbfiles = Array.to_list dbfiles in
   List.iter g#rm_f dbfiles;
 
-  (* 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 =
-      if inspect.i_uefi then
-        ("/boot/efi/EFI/redhat/grub.cfg", `Grub2) :: locations
-      else
-        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 ->
-      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 = Bootloaders.detect_bootloader g inspect in
 
   (* What kernel/kernel-like packages are installed on the current guest? *)
   let installed_kernels : kernel_info list =
@@ -270,88 +241,7 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source =
    * 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 (
@@ -390,21 +280,8 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source =
   (* 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 autorelabel () =
     (* Only do autorelabel if load_policy binary exists.  Actually
@@ -741,44 +618,7 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source =
     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)
+    bootloader#set_default kernel.ki_vmlinuz
 
   (* Even though the kernel was already installed (this version of
    * virt-v2v does not install new kernels), it could have an
@@ -940,26 +780,7 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source =
     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
+    bootloader#configure_console ()
 
   (* If the target doesn't support a serial console, we want to remove
    * all references to it instead.
@@ -990,69 +811,8 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source =
     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
+    bootloader#remove_console ()
 
-      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\\3" grub_cmdline
-          else
-            Str.global_replace rex "\\1\\3" 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. *)
@@ -1273,19 +1033,9 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source =
     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 =
@@ -1361,9 +1111,7 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source =
     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 |]);
+      bootloader#update ();
 
       Linux.augeas_reload g
     );
-- 
2.1.0




More information about the Libguestfs mailing list