[Libguestfs] [PATCH] v2v: linux: Move kernel detection to a separate module.

Richard W.M. Jones rjones at redhat.com
Fri Sep 9 15:11:53 UTC 2016


Create a new module [Linux_kernels] which does all kernel detection,
and also provides a place to define the kernel_info data structure.

This is essentially just code motion.
---
 v2v/Makefile.am       |   2 +
 v2v/convert_linux.ml  | 219 +++--------------------------------------------
 v2v/linux_kernels.ml  | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++
 v2v/linux_kernels.mli |  50 +++++++++++
 4 files changed, 295 insertions(+), 206 deletions(-)
 create mode 100644 v2v/linux_kernels.ml
 create mode 100644 v2v/linux_kernels.mli

diff --git a/v2v/Makefile.am b/v2v/Makefile.am
index 34f7976..a2f6969 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -41,6 +41,7 @@ SOURCES_MLI = \
 	inspect_source.mli \
 	linux.mli \
 	linux_bootloaders.mli \
+	linux_kernels.mli \
 	modules_list.mli \
 	name_from_disk.mli \
 	output_glance.mli \
@@ -85,6 +86,7 @@ SOURCES_ML = \
 	input_libvirt.ml \
 	input_ova.ml \
 	linux_bootloaders.ml \
+	linux_kernels.ml \
 	convert_linux.ml \
 	convert_windows.ml \
 	output_null.ml \
diff --git a/v2v/convert_linux.ml b/v2v/convert_linux.ml
index b85406e..500e060 100644
--- a/v2v/convert_linux.ml
+++ b/v2v/convert_linux.ml
@@ -33,31 +33,10 @@ open Common_utils
 
 open Utils
 open Types
+open Linux_kernels
 
 module G = Guestfs
 
-(* Kernel information. *)
-type kernel_info = {
-  ki_app : G.application2;         (* The RPM package data. *)
-  ki_name : string;                (* eg. "kernel-PAE" *)
-  ki_version : string;             (* version-release *)
-  ki_arch : string;                (* Kernel architecture. *)
-  ki_vmlinuz : string;             (* The path of the vmlinuz file. *)
-  ki_vmlinuz_stat : G.statns;      (* stat(2) of vmlinuz *)
-  ki_initrd : string option;       (* Path of initramfs, if found. *)
-  ki_modpath : string;             (* The module path. *)
-  ki_modules : string list;        (* The list of module names. *)
-  ki_supports_virtio : bool;       (* Kernel has virtio drivers? *)
-  ki_is_xen_kernel : bool;         (* Is a Xen paravirt kernel? *)
-  ki_is_debug : bool;              (* Is debug kernel? *)
-}
-
-let string_of_kernel_info ki =
-  sprintf "(%s, %s, %s, %s, %s, virtio=%b, xen=%b, debug=%b)"
-    ki.ki_name ki.ki_version ki.ki_arch ki.ki_vmlinuz
-    (match ki.ki_initrd with None -> "None" | Some f -> f)
-    ki.ki_supports_virtio ki.ki_is_xen_kernel ki.ki_is_debug
-
 (* The conversion function. *)
 let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps =
   (*----------------------------------------------------------------------*)
@@ -92,193 +71,21 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps =
   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 =
-    let rex_ko = Str.regexp ".*\\.k?o\\(\\.xz\\)?$" in
-    let rex_ko_extract = Str.regexp ".*/\\([^/]+\\)\\.k?o\\(\\.xz\\)?$" in
-    let rex_initrd =
-      if family = `Debian_family then
-        Str.regexp "^initrd.img-.*$"
-      else
-        Str.regexp "^initr\\(d\\|amfs\\)-.*\\(\\.img\\)?$" in
-    filter_map (
-      function
-      | { G.app2_name = name } as app
-          when name = "kernel" || String.is_prefix name "kernel-"
-               || String.is_prefix name "linux-image-" ->
-        (try
-           (* For each kernel, list the files directly owned by the kernel. *)
-           let files = Linux.file_list_of_package g inspect app in
-
-           if files = [] then (
-             warning (f_"package '%s' contains no files") name;
-             None
-           )
-           else (
-             (* Which of these is the kernel itself? *)
-             let vmlinuz = List.find (
-               fun filename -> String.is_prefix filename "/boot/vmlinuz-"
-             ) files in
-             (* Which of these is the modpath? *)
-             let modpath = List.find (
-               fun filename ->
-                 String.length filename >= 14 &&
-                   String.is_prefix filename "/lib/modules/"
-             ) files in
-
-             (* Check vmlinuz & modpath exist. *)
-             if not (g#is_dir ~followsymlinks:true modpath) then
-               raise Not_found;
-             let vmlinuz_stat =
-               try g#statns vmlinuz with G.Error _ -> raise Not_found in
-
-             (* Get/construct the version.  XXX Read this from kernel file. *)
-             let version =
-               let prefix_len = String.length "/lib/modules/" in
-               String.sub modpath prefix_len (String.length modpath - prefix_len) in
-
-             (* Find the initramfs which corresponds to the kernel.
-              * Since the initramfs is built at runtime, and doesn't have
-              * to be covered by the RPM file list, this is basically
-              * guesswork.
-              *)
-             let initrd =
-               let files = g#ls "/boot" in
-               let files = Array.to_list files in
-               let files =
-                 List.filter (fun n -> Str.string_match rex_initrd n 0) files in
-               let files =
-                 List.filter (
-                   fun n ->
-                     String.find n version >= 0
-                 ) files in
-               (* Don't consider kdump initramfs images (RHBZ#1138184). *)
-               let files =
-                 List.filter (fun n -> String.find n "kdump" == -1) files in
-               (* If several files match, take the shortest match.  This
-                * handles the case where we have a mix of same-version non-Xen
-                * and Xen kernels:
-                *   initrd-2.6.18-308.el5.img
-                *   initrd-2.6.18-308.el5xen.img
-                * and kernel 2.6.18-308.el5 (non-Xen) will match both
-                * (RHBZ#1141145).
-                *)
-               let cmp a b = compare (String.length a) (String.length b) in
-               let files = List.sort cmp files in
-               match files with
-               | [] ->
-                 warning (f_"no initrd was found in /boot matching %s %s.")
-                   name version;
-                 None
-               | x :: _ -> Some ("/boot/" ^ x) in
-
-             (* Get all modules, which might include custom-installed
-              * modules that don't appear in 'files' list above.
-              *)
-             let modules = g#find modpath in
-             let modules = Array.to_list modules in
-             let modules =
-               List.filter (fun m -> Str.string_match rex_ko m 0) modules in
-             assert (List.length modules > 0);
-
-             (* Determine the kernel architecture by looking at the
-              * architecture of an arbitrary kernel module.
-              *)
-             let arch =
-               let any_module = modpath ^ List.hd modules in
-               g#file_architecture any_module in
-
-             (* Just return the module names, without path or extension. *)
-             let modules = filter_map (
-               fun m ->
-                 if Str.string_match rex_ko_extract m 0 then
-                   Some (Str.matched_group 1 m)
-                 else
-                   None
-             ) modules in
-             assert (List.length modules > 0);
-
-             let supports_virtio = List.mem "virtio_net" modules in
-             let is_xen_kernel = List.mem "xennet" modules in
-
-             (* If the package name is like "kernel-debug", then it's
-              * a debug kernel.
-              *)
-             let is_debug =
-               String.is_suffix app.G.app2_name "-debug" ||
-               String.is_suffix app.G.app2_name "-dbg" in
-
-             Some {
-               ki_app  = app;
-               ki_name = name;
-               ki_version = version;
-               ki_arch = arch;
-               ki_vmlinuz = vmlinuz;
-               ki_vmlinuz_stat = vmlinuz_stat;
-               ki_initrd = initrd;
-               ki_modpath = modpath;
-               ki_modules = modules;
-               ki_supports_virtio = supports_virtio;
-               ki_is_xen_kernel = is_xen_kernel;
-               ki_is_debug = is_debug;
-             }
-           )
-
-         with Not_found -> None
-        )
-
-      | _ -> None
-    ) inspect.i_apps in
-
-  if verbose () then (
-    eprintf "installed kernel packages in this guest:\n";
-    List.iter (
-      fun kernel -> eprintf "\t%s\n" (string_of_kernel_info kernel)
-    ) installed_kernels;
-    flush stderr
-  );
-
-  if installed_kernels = [] then
-    error (f_"no installed kernel packages were found.\n\nThis probably indicates that %s was unable to inspect this guest properly.")
-      prog;
-
-  (* Now the difficult bit.  Get the grub kernels.  The first in this
-   * list is the default booting kernel.
-   *)
-  let grub_kernels : kernel_info list =
-    let vmlinuzes = bootloader#list_kernels in
-
-    (* Map these to installed kernels. *)
-    filter_map (
-      fun vmlinuz ->
-        try
-          let statbuf = g#statns vmlinuz in
-          let kernel =
-            List.find (
-              fun { ki_vmlinuz_stat = s } ->
-                statbuf.G.st_dev = s.G.st_dev && statbuf.G.st_ino = s.G.st_ino
-            ) installed_kernels in
-          Some kernel
-        with
-        | Not_found -> None
-        | G.Error msg as exn ->
-          (* If it isn't "no such file or directory", then re-raise it. *)
-          if g#last_errno () <> G.Errno.errno_ENOENT then raise exn;
-          warning (f_"ignoring kernel %s in grub, as it does not exist.")
-            vmlinuz;
-          None
-    ) vmlinuzes in
+  (* Detect which kernels are installed and offered by the bootloader. *)
+  let bootloader_kernels =
+    Linux_kernels.detect_kernels g inspect family bootloader in
 
   if verbose () then (
-    eprintf "grub kernels in this guest (first in list is default):\n";
+    eprintf "kernels offered by the bootloader in this guest (first in list is default):\n";
     List.iter (
-      fun kernel -> eprintf "\t%s\n" (string_of_kernel_info kernel)
-    ) grub_kernels;
+      fun kernel ->
+        eprintf "\t%s\n" (Linux_kernels.string_of_kernel_info kernel)
+    ) bootloader_kernels;
     flush stderr
   );
 
-  if grub_kernels = [] then
-    error (f_"no kernels were found in the grub configuration.\n\nThis probably indicates that %s was unable to parse the grub configuration of this guest.")
+  if bootloader_kernels = [] then
+    error (f_"no kernels were found in the bootloader configuration.\n\nThis probably indicates that %s was unable to parse the grub configuration of this guest.")
       prog;
 
   (*----------------------------------------------------------------------*)
@@ -592,7 +399,7 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps =
     (* Check a non-Xen kernel exists. *)
     let only_xen_kernels = List.for_all (
       fun { ki_is_xen_kernel = is_xen_kernel } -> is_xen_kernel
-    ) grub_kernels in
+    ) bootloader_kernels in
     if only_xen_kernels then
       error (f_"only Xen kernels are installed in this guest.\n\nRead the %s(1) manual, section \"XEN PARAVIRTUALIZED GUESTS\", to see what to do.") prog;
 
@@ -610,12 +417,12 @@ let rec convert ~keep_serial_console (g : G.guestfs) inspect source rcaps =
           else compare k2.ki_is_debug k1.ki_is_debug
         )
       in
-      let kernels = grub_kernels in
+      let kernels = bootloader_kernels in
       let kernels = List.filter (fun { ki_is_xen_kernel = is_xen_kernel } -> not is_xen_kernel) kernels in
       let kernels = List.sort compare_best_kernels kernels in
       let kernels = List.rev kernels (* so best is first *) in
       List.hd kernels in
-    if best_kernel <> List.hd grub_kernels then
+    if best_kernel <> List.hd bootloader_kernels then
       bootloader#set_default_kernel best_kernel.ki_vmlinuz;
 
     (* Does the best/bootable kernel support virtio? *)
diff --git a/v2v/linux_kernels.ml b/v2v/linux_kernels.ml
new file mode 100644
index 0000000..213e495
--- /dev/null
+++ b/v2v/linux_kernels.ml
@@ -0,0 +1,230 @@
+(* 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.
+ *)
+
+(* Detect which kernels are installed and offered by the bootloader. *)
+
+open Printf
+
+open Common_gettext.Gettext
+open Common_utils
+
+open Types
+
+module G = Guestfs
+
+(* Kernel information. *)
+type kernel_info = {
+  ki_app : G.application2;
+  ki_name : string;
+  ki_version : string;
+  ki_arch : string;
+  ki_vmlinuz : string;
+  ki_vmlinuz_stat : G.statns;
+  ki_initrd : string option;
+  ki_modpath : string;
+  ki_modules : string list;
+  ki_supports_virtio : bool;
+  ki_is_xen_kernel : bool;
+  ki_is_debug : bool;
+}
+
+let string_of_kernel_info ki =
+  sprintf "(%s, %s, %s, %s, %s, virtio=%b, xen=%b, debug=%b)"
+    ki.ki_name ki.ki_version ki.ki_arch ki.ki_vmlinuz
+    (match ki.ki_initrd with None -> "None" | Some f -> f)
+    ki.ki_supports_virtio ki.ki_is_xen_kernel ki.ki_is_debug
+
+let detect_kernels g inspect family bootloader =
+  (* What kernel/kernel-like packages are installed on the current guest? *)
+  let installed_kernels : kernel_info list =
+    let rex_ko = Str.regexp ".*\\.k?o\\(\\.xz\\)?$" in
+    let rex_ko_extract = Str.regexp ".*/\\([^/]+\\)\\.k?o\\(\\.xz\\)?$" in
+    let rex_initrd =
+      if family = `Debian_family then
+        Str.regexp "^initrd.img-.*$"
+      else
+        Str.regexp "^initr\\(d\\|amfs\\)-.*\\(\\.img\\)?$" in
+    filter_map (
+      function
+      | { G.app2_name = name } as app
+          when name = "kernel" || String.is_prefix name "kernel-"
+               || String.is_prefix name "linux-image-" ->
+        (try
+           (* For each kernel, list the files directly owned by the kernel. *)
+           let files = Linux.file_list_of_package g inspect app in
+
+           if files = [] then (
+             warning (f_"package '%s' contains no files") name;
+             None
+           )
+           else (
+             (* Which of these is the kernel itself? *)
+             let vmlinuz = List.find (
+               fun filename -> String.is_prefix filename "/boot/vmlinuz-"
+             ) files in
+             (* Which of these is the modpath? *)
+             let modpath = List.find (
+               fun filename ->
+                 String.length filename >= 14 &&
+                   String.is_prefix filename "/lib/modules/"
+             ) files in
+
+             (* Check vmlinuz & modpath exist. *)
+             if not (g#is_dir ~followsymlinks:true modpath) then
+               raise Not_found;
+             let vmlinuz_stat =
+               try g#statns vmlinuz with G.Error _ -> raise Not_found in
+
+             (* Get/construct the version.  XXX Read this from kernel file. *)
+             let version =
+               let prefix_len = String.length "/lib/modules/" in
+               String.sub modpath prefix_len (String.length modpath - prefix_len) in
+
+             (* Find the initramfs which corresponds to the kernel.
+              * Since the initramfs is built at runtime, and doesn't have
+              * to be covered by the RPM file list, this is basically
+              * guesswork.
+              *)
+             let initrd =
+               let files = g#ls "/boot" in
+               let files = Array.to_list files in
+               let files =
+                 List.filter (fun n -> Str.string_match rex_initrd n 0) files in
+               let files =
+                 List.filter (
+                   fun n ->
+                     String.find n version >= 0
+                 ) files in
+               (* Don't consider kdump initramfs images (RHBZ#1138184). *)
+               let files =
+                 List.filter (fun n -> String.find n "kdump" == -1) files in
+               (* If several files match, take the shortest match.  This
+                * handles the case where we have a mix of same-version non-Xen
+                * and Xen kernels:
+                *   initrd-2.6.18-308.el5.img
+                *   initrd-2.6.18-308.el5xen.img
+                * and kernel 2.6.18-308.el5 (non-Xen) will match both
+                * (RHBZ#1141145).
+                *)
+               let cmp a b = compare (String.length a) (String.length b) in
+               let files = List.sort cmp files in
+               match files with
+               | [] ->
+                 warning (f_"no initrd was found in /boot matching %s %s.")
+                   name version;
+                 None
+               | x :: _ -> Some ("/boot/" ^ x) in
+
+             (* Get all modules, which might include custom-installed
+              * modules that don't appear in 'files' list above.
+              *)
+             let modules = g#find modpath in
+             let modules = Array.to_list modules in
+             let modules =
+               List.filter (fun m -> Str.string_match rex_ko m 0) modules in
+             assert (List.length modules > 0);
+
+             (* Determine the kernel architecture by looking at the
+              * architecture of an arbitrary kernel module.
+              *)
+             let arch =
+               let any_module = modpath ^ List.hd modules in
+               g#file_architecture any_module in
+
+             (* Just return the module names, without path or extension. *)
+             let modules = filter_map (
+               fun m ->
+                 if Str.string_match rex_ko_extract m 0 then
+                   Some (Str.matched_group 1 m)
+                 else
+                   None
+             ) modules in
+             assert (List.length modules > 0);
+
+             let supports_virtio = List.mem "virtio_net" modules in
+             let is_xen_kernel = List.mem "xennet" modules in
+
+             (* If the package name is like "kernel-debug", then it's
+              * a debug kernel.
+              *)
+             let is_debug =
+               String.is_suffix app.G.app2_name "-debug" ||
+               String.is_suffix app.G.app2_name "-dbg" in
+
+             Some {
+               ki_app  = app;
+               ki_name = name;
+               ki_version = version;
+               ki_arch = arch;
+               ki_vmlinuz = vmlinuz;
+               ki_vmlinuz_stat = vmlinuz_stat;
+               ki_initrd = initrd;
+               ki_modpath = modpath;
+               ki_modules = modules;
+               ki_supports_virtio = supports_virtio;
+               ki_is_xen_kernel = is_xen_kernel;
+               ki_is_debug = is_debug;
+             }
+           )
+
+         with Not_found -> None
+        )
+
+      | _ -> None
+    ) inspect.i_apps in
+
+  if verbose () then (
+    eprintf "installed kernel packages in this guest:\n";
+    List.iter (
+      fun kernel -> eprintf "\t%s\n" (string_of_kernel_info kernel)
+    ) installed_kernels;
+    flush stderr
+  );
+
+  if installed_kernels = [] then
+    error (f_"no installed kernel packages were found.\n\nThis probably indicates that %s was unable to inspect this guest properly.")
+      prog;
+
+  (* Now the difficult bit.  Get the grub kernels.  The first in this
+   * list is the default booting kernel.
+   *)
+  let grub_kernels : kernel_info list =
+    let vmlinuzes = bootloader#list_kernels in
+
+    (* Map these to installed kernels. *)
+    filter_map (
+      fun vmlinuz ->
+        try
+          let statbuf = g#statns vmlinuz in
+          let kernel =
+            List.find (
+              fun { ki_vmlinuz_stat = s } ->
+                statbuf.G.st_dev = s.G.st_dev && statbuf.G.st_ino = s.G.st_ino
+            ) installed_kernels in
+          Some kernel
+        with
+        | Not_found -> None
+        | G.Error msg as exn ->
+          (* If it isn't "no such file or directory", then re-raise it. *)
+          if g#last_errno () <> G.Errno.errno_ENOENT then raise exn;
+          warning (f_"ignoring kernel %s in grub, as it does not exist.")
+            vmlinuz;
+          None
+    ) vmlinuzes in
+
+  grub_kernels
diff --git a/v2v/linux_kernels.mli b/v2v/linux_kernels.mli
new file mode 100644
index 0000000..ea2a3b6
--- /dev/null
+++ b/v2v/linux_kernels.mli
@@ -0,0 +1,50 @@
+(* 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.
+ *)
+
+(** Detect which kernels are installed and offered by the bootloader. *)
+
+type kernel_info = {
+  ki_app : Guestfs.application2;   (** The RPM package data. *)
+  ki_name : string;                (** eg. "kernel-PAE" *)
+  ki_version : string;             (** version-release *)
+  ki_arch : string;                (** Kernel architecture. *)
+  ki_vmlinuz : string;             (** The path of the vmlinuz file. *)
+  ki_vmlinuz_stat : Guestfs.statns;(** stat(2) of vmlinuz *)
+  ki_initrd : string option;       (** Path of initramfs, if found. *)
+  ki_modpath : string;             (** The module path. *)
+  ki_modules : string list;        (** The list of module names. *)
+  ki_supports_virtio : bool;       (** Kernel has virtio drivers? *)
+  ki_is_xen_kernel : bool;         (** Is a Xen paravirt kernel? *)
+  ki_is_debug : bool;              (** Is debug kernel? *)
+}
+(** Kernel information. *)
+
+val string_of_kernel_info : kernel_info -> string
+(** Display {!kernel_info} struct, eg. for debugging. *)
+
+val detect_kernels : Guestfs.guestfs -> Types.inspect ->
+                     [> `Debian_family ] ->
+                     < list_kernels : string list; .. > ->
+                     kernel_info list
+(** This function detects the kernels offered by the Linux
+    bootloader (eg. grub).
+
+    It will only return the intersection of kernels that are
+    installed and kernels that grub knows about.  The first
+    kernel in the returned list is the default boot option, ie.
+    what the guest would boot without interaction or overrides. *)
-- 
2.9.3




More information about the Libguestfs mailing list