[Libguestfs] [PATCH 4/4] v2v: Pass CPU vendor, model and topology from source to target.

Richard W.M. Jones rjones at redhat.com
Thu Mar 16 18:57:15 UTC 2017


Where supported, pass the source CPU vendor, model and topology to the
target hypervisor.

For -i ova, we can get just cores per socket via a proprietary VMware
extension to OVF.

For -i libvirt and from virt-p2v, we can get all of these fields from
the libvirt XML.

For -o libvirt/local, we can preserve all of the information in the
target XML.

For -o glance, as far as I can tell from the documentation, Glance
does not support anything like this.

For -o rhv/vdsm, it looks from the code like this could be supported,
but I could not work out how to enable it in the OVF.

For -o qemu, we preserve the topology only because versions of qemu
vary widely in their support for CPU models.
---
 v2v/create_libvirt_xml.ml              | 36 ++++++++++++++++++++++++++++++++++
 v2v/input_disk.ml                      |  5 +++++
 v2v/input_ova.ml                       |  8 +++++++-
 v2v/output_qemu.ml                     | 24 +++++++++++++++++++++--
 v2v/parse_libvirt_xml.ml               | 11 +++++++++++
 v2v/parse_ovf_from_ova.ml              | 22 ++++++++++++++++++++-
 v2v/parse_ovf_from_ova.mli             |  5 +++--
 v2v/test-v2v-i-ova-formats.expected    |  3 +++
 v2v/test-v2v-i-ova-gz.expected         |  3 +++
 v2v/test-v2v-i-ova-subfolders.expected |  3 +++
 v2v/test-v2v-i-ova-tar.expected        |  3 +++
 v2v/test-v2v-i-ova-two-disks.expected  |  3 +++
 v2v/test-v2v-print-source.expected     |  5 ++++-
 v2v/test-v2v-print-source.xml          | 10 ++++++++++
 v2v/types.ml                           | 13 ++++++++++++
 v2v/types.mli                          |  5 +++++
 v2v/v2v.ml                             | 27 +++++++++++++++++++++++++
 17 files changed, 179 insertions(+), 7 deletions(-)

diff --git a/v2v/create_libvirt_xml.ml b/v2v/create_libvirt_xml.ml
index 7830bc3..fc71965 100644
--- a/v2v/create_libvirt_xml.ml
+++ b/v2v/create_libvirt_xml.ml
@@ -47,6 +47,42 @@ let create_libvirt_xml ?pool source target_buses guestcaps
     e "vcpu" [] [PCData (string_of_int source.s_vcpu)]
   ];
 
+  if source.s_cpu_vendor <> None || source.s_cpu_model <> None ||
+     source.s_cpu_sockets <> None || source.s_cpu_cores <> None ||
+     source.s_cpu_threads <> None then (
+    let cpu = ref [] in
+
+    (match source.s_cpu_vendor with
+     | None -> ()
+     | Some vendor ->
+        push_back cpu (e "vendor" [] [PCData vendor])
+    );
+    (match source.s_cpu_model with
+     | None -> ()
+     | Some model ->
+        push_back cpu (e "model" ["fallback", "allow"] [PCData model])
+    );
+    if source.s_cpu_sockets <> None || source.s_cpu_cores <> None ||
+       source.s_cpu_threads <> None then (
+      let topology_attrs = ref [] in
+      (match source.s_cpu_sockets with
+       | None -> ()
+       | Some v -> push_back topology_attrs ("sockets", string_of_int v)
+      );
+      (match source.s_cpu_cores with
+       | None -> ()
+       | Some v -> push_back topology_attrs ("cores", string_of_int v)
+      );
+      (match source.s_cpu_threads with
+       | None -> ()
+       | Some v -> push_back topology_attrs ("threads", string_of_int v)
+      );
+      push_back cpu (e "topology" !topology_attrs [])
+    );
+
+    append body [ e "cpu" [ "match", "minimum" ] !cpu ]
+  );
+
   let uefi_firmware =
     match target_firmware with
     | TargetBIOS -> None
diff --git a/v2v/input_disk.ml b/v2v/input_disk.ml
index 27f8553..d28f45e 100644
--- a/v2v/input_disk.ml
+++ b/v2v/input_disk.ml
@@ -80,6 +80,11 @@ class input_disk input_format disk = object
       s_name = name; s_orig_name = name;
       s_memory = 2048L *^ 1024L *^ 1024L; (* 2048 MB *)
       s_vcpu = 1;                         (* 1 vCPU is a safe default *)
+      s_cpu_vendor = None;
+      s_cpu_model = None;
+      s_cpu_sockets = None;
+      s_cpu_cores = None;
+      s_cpu_threads = None;
       s_features = [ "acpi"; "apic"; "pae" ];
       s_firmware = UnknownFirmware;       (* causes virt-v2v to autodetect *)
       s_display =
diff --git a/v2v/input_ova.ml b/v2v/input_ova.ml
index e80ec82..b82862f 100644
--- a/v2v/input_ova.ml
+++ b/v2v/input_ova.ml
@@ -222,7 +222,8 @@ object
     let ovf_folder = Filename.dirname ovf in
 
     (* Parse the ovf file. *)
-    let name, memory, vcpu, firmware, disks, removables, nics =
+    let name, memory, vcpu, cpu_sockets, cpu_cores, firmware,
+        disks, removables, nics =
       parse_ovf_from_ova ovf in
 
     let name =
@@ -314,6 +315,11 @@ object
       s_orig_name = name;
       s_memory = memory;
       s_vcpu = vcpu;
+      s_cpu_vendor = None;
+      s_cpu_model = None;
+      s_cpu_sockets = cpu_sockets;
+      s_cpu_cores = cpu_cores;
+      s_cpu_threads = None; (* XXX *)
       s_features = []; (* XXX *)
       s_firmware = firmware;
       s_display = None; (* XXX *)
diff --git a/v2v/output_qemu.ml b/v2v/output_qemu.ml
index 84efd45..b3115f9 100644
--- a/v2v/output_qemu.ml
+++ b/v2v/output_qemu.ml
@@ -96,8 +96,28 @@ object
     );
 
     arg "-m" (Int64.to_string (source.s_memory /^ 1024L /^ 1024L));
-    if source.s_vcpu > 1 then
-      arg "-smp" (string_of_int source.s_vcpu);
+    if source.s_vcpu > 1 then (
+      if source.s_cpu_sockets <> None || source.s_cpu_cores <> None ||
+         source.s_cpu_threads <> None then (
+        let a = ref [] in
+        push_back a (sprintf "cpus=%d" source.s_vcpu);
+        (match source.s_cpu_sockets with
+         | None -> ()
+         | Some v -> push_back a (sprintf "sockets=%d" v)
+        );
+        (match source.s_cpu_cores with
+         | None -> ()
+         | Some v -> push_back a (sprintf "cores=%d" v)
+        );
+        (match source.s_cpu_threads with
+         | None -> ()
+         | Some v -> push_back a (sprintf "threads=%d" v)
+        );
+        commas "-smp" !a
+      )
+      else
+        arg "-smp" (string_of_int source.s_vcpu);
+    );
 
     let make_disk if_name i = function
     | BusSlotEmpty -> ()
diff --git a/v2v/parse_libvirt_xml.ml b/v2v/parse_libvirt_xml.ml
index edffd20..6032c31 100644
--- a/v2v/parse_libvirt_xml.ml
+++ b/v2v/parse_libvirt_xml.ml
@@ -67,6 +67,12 @@ let parse_libvirt_xml ?conn xml =
   let memory = memory *^ 1024L in
   let vcpu = xpath_int_default "/domain/vcpu/text()" 1 in
 
+  let cpu_vendor = xpath_string "/domain/cpu/vendor/text()" in
+  let cpu_model = xpath_string "/domain/cpu/model/text()" in
+  let cpu_sockets = xpath_int "/domain/cpu/topology/@sockets" in
+  let cpu_cores = xpath_int "/domain/cpu/topology/@cores" in
+  let cpu_threads = xpath_int "/domain/cpu/topology/@threads" in
+
   let features =
     let features = ref [] in
     let obj = Xml.xpath_eval_expression xpathctx "/domain/features/*" in
@@ -410,6 +416,11 @@ let parse_libvirt_xml ?conn xml =
     s_name = name; s_orig_name = name;
     s_memory = memory;
     s_vcpu = vcpu;
+    s_cpu_vendor = cpu_vendor;
+    s_cpu_model = cpu_model;
+    s_cpu_sockets = cpu_sockets;
+    s_cpu_cores = cpu_cores;
+    s_cpu_threads = cpu_threads;
     s_features = features;
     s_firmware = UnknownFirmware; (* XXX until RHBZ#1217444 is fixed *)
     s_display = display;
diff --git a/v2v/parse_ovf_from_ova.ml b/v2v/parse_ovf_from_ova.ml
index 989483e..2a37527 100644
--- a/v2v/parse_ovf_from_ova.ml
+++ b/v2v/parse_ovf_from_ova.ml
@@ -69,6 +69,26 @@ let parse_ovf_from_ova ovf_filename =
     (* Search for number of vCPUs. *)
     let vcpu = xpath_int_default "/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=3]/rasd:VirtualQuantity/text()" 1 in
 
+    (* CPU topology.  coresPerSocket is a VMware proprietary extension.
+     * I couldn't find out how hyperthreads is specified in the OVF.
+     *)
+    let cores_per_socket = xpath_int "/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/ovf:Item[rasd:ResourceType/text()=3]/vmw:CoresPerSocket/text()" in
+    let cpu_sockets, cpu_cores =
+      match cores_per_socket with
+      | None -> None, None
+      | Some cores_per_socket when cores_per_socket <= 0 ->
+         warning (f_"invalid vmw:CoresPerSocket (%d) ignored")
+                 cores_per_socket;
+         None, None
+      | Some cores_per_socket ->
+         let sockets = vcpu / cores_per_socket in
+         if sockets <= 0 then (
+           warning (f_"invalid vmw:CoresPerSocket < number of cores");
+           None, None
+         )
+         else
+           Some sockets, Some cores_per_socket in
+
     (* BIOS or EFI firmware? *)
     let firmware = xpath_string_default "/ovf:Envelope/ovf:VirtualSystem/ovf:VirtualHardwareSection/vmw:Config[@vmw:key=\"firmware\"]/@vmw:value" "bios" in
     let firmware =
@@ -78,7 +98,7 @@ let parse_ovf_from_ova ovf_filename =
       | s ->
          error (f_"unknown Config:firmware value %s (expected \"bios\" or \"efi\")") s in
 
-    name, memory, vcpu, firmware,
+    name, memory, vcpu, cpu_sockets, cpu_cores, firmware,
     parse_disks (), parse_removables (), parse_nics ()
 
   (* Helper function to return the parent controller of a disk. *)
diff --git a/v2v/parse_ovf_from_ova.mli b/v2v/parse_ovf_from_ova.mli
index 3f60abc..54cdcf2 100644
--- a/v2v/parse_ovf_from_ova.mli
+++ b/v2v/parse_ovf_from_ova.mli
@@ -29,8 +29,9 @@ type ovf_disk = {
 }
 (** A VMDK disk from a parsed OVF. *)
 
-val parse_ovf_from_ova : string -> string option * int64 * int * Types.source_firmware * ovf_disk list * Types.source_removable list * Types.source_nic list
+val parse_ovf_from_ova : string -> string option * int64 * int * int option * int option * Types.source_firmware * ovf_disk list * Types.source_removable list * Types.source_nic list
 (** Parse an OVF file.
 
     The returned tuple is
-    [name, memory, vcpu, firmware, disks, removables, nics] *)
+    [name, memory, vcpu, cpu_sockets, cpu_cores, firmware,
+    disks, removables, nics] *)
diff --git a/v2v/test-v2v-i-ova-formats.expected b/v2v/test-v2v-i-ova-formats.expected
index 7049aee..11b24e0 100644
--- a/v2v/test-v2v-i-ova-formats.expected
+++ b/v2v/test-v2v-i-ova-formats.expected
@@ -4,6 +4,9 @@ Source guest information (--print-source option):
 hypervisor type: vmware
          memory: 1073741824 (bytes)
        nr vCPUs: 1
+     CPU vendor: 
+      CPU model: 
+   CPU topology: sockets: - cores/socket: - threads/core: -
    CPU features: 
        firmware: uefi
         display: 
diff --git a/v2v/test-v2v-i-ova-gz.expected b/v2v/test-v2v-i-ova-gz.expected
index 50ba746..11db2a3 100644
--- a/v2v/test-v2v-i-ova-gz.expected
+++ b/v2v/test-v2v-i-ova-gz.expected
@@ -4,6 +4,9 @@ Source guest information (--print-source option):
 hypervisor type: vmware
          memory: 1073741824 (bytes)
        nr vCPUs: 1
+     CPU vendor: 
+      CPU model: 
+   CPU topology: sockets: - cores/socket: - threads/core: -
    CPU features: 
        firmware: bios
         display: 
diff --git a/v2v/test-v2v-i-ova-subfolders.expected b/v2v/test-v2v-i-ova-subfolders.expected
index b6fdb07..4ef8b4b 100644
--- a/v2v/test-v2v-i-ova-subfolders.expected
+++ b/v2v/test-v2v-i-ova-subfolders.expected
@@ -4,6 +4,9 @@ Source guest information (--print-source option):
 hypervisor type: vmware
          memory: 1073741824 (bytes)
        nr vCPUs: 1
+     CPU vendor: 
+      CPU model: 
+   CPU topology: sockets: - cores/socket: - threads/core: -
    CPU features: 
        firmware: uefi
         display: 
diff --git a/v2v/test-v2v-i-ova-tar.expected b/v2v/test-v2v-i-ova-tar.expected
index 7049aee..11b24e0 100644
--- a/v2v/test-v2v-i-ova-tar.expected
+++ b/v2v/test-v2v-i-ova-tar.expected
@@ -4,6 +4,9 @@ Source guest information (--print-source option):
 hypervisor type: vmware
          memory: 1073741824 (bytes)
        nr vCPUs: 1
+     CPU vendor: 
+      CPU model: 
+   CPU topology: sockets: - cores/socket: - threads/core: -
    CPU features: 
        firmware: uefi
         display: 
diff --git a/v2v/test-v2v-i-ova-two-disks.expected b/v2v/test-v2v-i-ova-two-disks.expected
index cc850a7..b0bb3ef 100644
--- a/v2v/test-v2v-i-ova-two-disks.expected
+++ b/v2v/test-v2v-i-ova-two-disks.expected
@@ -4,6 +4,9 @@ Source guest information (--print-source option):
 hypervisor type: vmware
          memory: 1073741824 (bytes)
        nr vCPUs: 1
+     CPU vendor: 
+      CPU model: 
+   CPU topology: sockets: - cores/socket: - threads/core: -
    CPU features: 
        firmware: bios
         display: 
diff --git a/v2v/test-v2v-print-source.expected b/v2v/test-v2v-print-source.expected
index b947927..6e78aad 100644
--- a/v2v/test-v2v-print-source.expected
+++ b/v2v/test-v2v-print-source.expected
@@ -2,7 +2,10 @@
 hypervisor type: kvm
          memory: 1073741824 (bytes)
        nr vCPUs: 1
-   CPU features: 
+     CPU vendor: Intel
+      CPU model: Broadwell
+   CPU topology: sockets: 4 cores/socket: 8 threads/core: 2
+   CPU features: pae,apic,acpi
        firmware: unknown
         display: 
           video: qxl
diff --git a/v2v/test-v2v-print-source.xml b/v2v/test-v2v-print-source.xml
index 0667f2e..3768caf 100644
--- a/v2v/test-v2v-print-source.xml
+++ b/v2v/test-v2v-print-source.xml
@@ -1,6 +1,16 @@
 <domain type='kvm'>
   <name>windows</name>
   <memory>1048576</memory>
+  <cpu match="minimum">
+    <vendor>Intel</vendor>
+    <model fallback="allow">Broadwell</model>
+    <topology sockets="4" cores="8" threads="2"/>
+  </cpu>
+  <features>
+    <acpi/>
+    <apic/>
+    <pae/>
+  </features>
   <os>
     <type>hvm</type>
     <boot dev='hd'/>
diff --git a/v2v/types.ml b/v2v/types.ml
index d802e19..31cbbd2 100644
--- a/v2v/types.ml
+++ b/v2v/types.ml
@@ -29,6 +29,11 @@ type source = {
   s_orig_name : string;
   s_memory : int64;
   s_vcpu : int;
+  s_cpu_vendor : string option;
+  s_cpu_model : string option;
+  s_cpu_sockets : int option;
+  s_cpu_cores : int option;
+  s_cpu_threads : int option;
   s_features : string list;
   s_firmware : source_firmware;
   s_display : source_display option;
@@ -102,6 +107,9 @@ let rec string_of_source s =
 hypervisor type: %s
          memory: %Ld (bytes)
        nr vCPUs: %d
+     CPU vendor: %s
+      CPU model: %s
+   CPU topology: sockets: %s cores/socket: %s threads/core: %s
    CPU features: %s
        firmware: %s
         display: %s
@@ -118,6 +126,11 @@ NICs:
     (string_of_source_hypervisor s.s_hypervisor)
     s.s_memory
     s.s_vcpu
+    (match s.s_cpu_vendor with None -> "" | Some v -> v)
+    (match s.s_cpu_model with None -> "" | Some v -> v)
+    (match s.s_cpu_sockets with None -> "-" | Some v -> string_of_int v)
+    (match s.s_cpu_cores with None -> "-" | Some v -> string_of_int v)
+    (match s.s_cpu_threads with None -> "-" | Some v -> string_of_int v)
     (String.concat "," s.s_features)
     (string_of_source_firmware s.s_firmware)
     (match s.s_display with
diff --git a/v2v/types.mli b/v2v/types.mli
index 31a974a..c902b7a 100644
--- a/v2v/types.mli
+++ b/v2v/types.mli
@@ -64,6 +64,11 @@ type source = {
                                             still saved here). *)
   s_memory : int64;                     (** Memory size (bytes). *)
   s_vcpu : int;                         (** Number of CPUs. *)
+  s_cpu_vendor : string option;         (** Source CPU vendor. *)
+  s_cpu_model : string option;          (** Source CPU model. *)
+  s_cpu_sockets : int option;           (** Number of sockets. *)
+  s_cpu_cores : int option;             (** Number of cores per socket. *)
+  s_cpu_threads : int option;           (** Number of threads per core. *)
   s_features : string list;             (** Machine features. *)
   s_firmware : source_firmware;         (** Firmware (BIOS or EFI). *)
   s_display : source_display option;    (** Guest display. *)
diff --git a/v2v/v2v.ml b/v2v/v2v.ml
index 551524d..bd3a413 100644
--- a/v2v/v2v.ml
+++ b/v2v/v2v.ml
@@ -181,7 +181,34 @@ and open_source cmdline input =
 
   assert (source.s_name <> "");
   assert (source.s_memory > 0L);
+
   assert (source.s_vcpu >= 1);
+  assert (source.s_cpu_vendor <> Some "");
+  assert (source.s_cpu_model <> Some "");
+  (match source.s_cpu_sockets with
+   | None -> ()
+   | Some i when i > 0 -> ()
+   | _ -> assert false);
+  (match source.s_cpu_cores with
+   | None -> ()
+   | Some i when i > 0 -> ()
+   | _ -> assert false);
+  (match source.s_cpu_threads with
+   | None -> ()
+   | Some i when i > 0 -> ()
+   | _ -> assert false);
+  (match source.s_cpu_sockets, source.s_cpu_cores, source.s_cpu_threads with
+   | None, None, None -> () (* no topology specified *)
+   | sockets, cores, threads ->
+      let sockets = match sockets with None -> 1 | Some v -> v in
+      let cores = match cores with None -> 1 | Some v -> v in
+      let threads = match threads with None -> 1 | Some v -> v in
+      let expected_vcpu = sockets * cores * threads in
+      if expected_vcpu <> source.s_vcpu then
+        warning (f_"source sockets * cores * threads <> number of vCPUs.\nSockets %d * cores per socket %d * threads %d = %d, but number of vCPUs = %d.\n\nThis is a problem with either the source metadata or the virt-v2v input module.  In some circumstances this could stop the guest from booting on the target.")
+                sockets cores threads expected_vcpu source.s_vcpu
+  );
+
   if source.s_disks = [] then
     error (f_"source has no hard disks!");
   List.iter (
-- 
2.10.2




More information about the Libguestfs mailing list