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

Richard W.M. Jones rjones at redhat.com
Fri Mar 17 11:34:01 UTC 2017


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

-i ova: We can get just cores per socket via a proprietary VMware
extension to OVF.

-i libvirt, virt-p2v: We can get all of these fields from the libvirt
XML.

-o libvirt, -o local: We can preserve all of the information in the
target XML.

-o rhv/vdsm: We can only pass through the topology, since neither
oVirt Engine nor the OVF format supports CPU vendor or model.
Topology uses an extension of OVF which is not part of the standard.

-o qemu: We preserve the topology only because versions of qemu vary
widely in their support for CPU models.

-o glance: Only the topology is preserved since that is all that
Glance currently supports.

Thanks: Pino Toscano.
---
 v2v/OVF.ml                             | 111 ++++++++++++++++++++++-----------
 v2v/create_libvirt_xml.ml              |  36 +++++++++++
 v2v/input_disk.ml                      |   5 ++
 v2v/input_ova.ml                       |   8 ++-
 v2v/output_glance.ml                   |  19 ++++++
 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 ++++++++
 19 files changed, 271 insertions(+), 45 deletions(-)

diff --git a/v2v/OVF.ml b/v2v/OVF.ml
index 1f838f5..5c2ebc9 100644
--- a/v2v/OVF.ml
+++ b/v2v/OVF.ml
@@ -318,52 +318,87 @@ let rec create_ovf source targets guestcaps inspect
                     (e "Origin" [] [PCData (string_of_int origin)])
       );
 
-      append content_subnodes [
+      push_back content_subnodes (
         e "Section" ["ovf:id", vm_uuid; "ovf:required", "false";
                      "xsi:type", "ovf:OperatingSystemSection_Type"] [
           e "Info" [] [PCData inspect.i_product_name];
           e "Description" [] [PCData ostype];
-        ];
-
-        e "Section" ["xsi:type", "ovf:VirtualHardwareSection_Type"] [
-          e "Info" [] [PCData (sprintf "%d CPU, %Ld Memory" source.s_vcpu memsize_mb)];
-          e "Item" [] [
-            e "rasd:Caption" [] [PCData (sprintf "%d virtual cpu" source.s_vcpu)];
-            e "rasd:Description" [] [PCData "Number of virtual CPU"];
-            e "rasd:InstanceId" [] [PCData "1"];
-            e "rasd:ResourceType" [] [PCData "3"];
-            e "rasd:num_of_sockets" [] [PCData (string_of_int source.s_vcpu)];
-            e "rasd:cpu_per_socket"[] [PCData "1"];
-          ];
-          e "Item" [] [
-            e "rasd:Caption" [] [PCData (sprintf "%Ld MB of memory" memsize_mb)];
-            e "rasd:Description" [] [PCData "Memory Size"];
-            e "rasd:InstanceId" [] [PCData "2"];
-            e "rasd:ResourceType" [] [PCData "4"];
-            e "rasd:AllocationUnits" [] [PCData "MegaBytes"];
-            e "rasd:VirtualQuantity" [] [PCData (Int64.to_string memsize_mb)];
-          ];
-          e "Item" [] [
-            e "rasd:Caption" [] [PCData "USB Controller"];
-            e "rasd:InstanceId" [] [PCData "3"];
-            e "rasd:ResourceType" [] [PCData "23"];
-            e "rasd:UsbPolicy" [] [PCData "Disabled"];
-          ];
-          (* We always add a qxl device when outputting to RHV.
-           * See RHBZ#1213701 and RHBZ#1211231 for the reasoning
-           * behind that.
-           *)
-          e "Item" [] [
-            e "rasd:Caption" [] [PCData "Graphical Controller"];
-            e "rasd:InstanceId" [] [PCData (uuidgen ())];
-            e "rasd:ResourceType" [] [PCData "20"];
-            e "Type" [] [PCData "video"];
-            e "rasd:VirtualQuantity" [] [PCData "1"];
-            e "rasd:Device" [] [PCData "qxl"];
           ]
+      );
+
+      let virtual_hardware_section_items = ref [
+        e "Info" [] [PCData (sprintf "%d CPU, %Ld Memory"
+                                     source.s_vcpu memsize_mb)]
+      ] in
+
+      push_back virtual_hardware_section_items (
+        e "Item" [] ([
+          e "rasd:Caption" [] [PCData (sprintf "%d virtual cpu" source.s_vcpu)];
+          e "rasd:Description" [] [PCData "Number of virtual CPU"];
+          e "rasd:InstanceId" [] [PCData "1"];
+          e "rasd:ResourceType" [] [PCData "3"]
+        ] @
+          if source.s_cpu_sockets <> None || source.s_cpu_cores <> None ||
+             source.s_cpu_threads <> None then (
+            let sockets =
+              match source.s_cpu_sockets with
+              | None -> "1"
+              | Some v -> string_of_int v in
+            let cores =
+              match source.s_cpu_cores with
+              | None -> "1"
+              | Some v -> string_of_int v in
+            let threads =
+              match source.s_cpu_threads with
+              | None -> "1"
+              | Some v -> string_of_int v in
+            [ e "rasd:num_of_sockets" [] [PCData sockets];
+              e "rasd:cpu_per_socket"[] [PCData cores];
+              e "rasd:threads_per_cpu"[] [PCData threads] ]
+          )
+          else (
+            [ e "rasd:num_of_sockets" [] [PCData "1"];
+              e "rasd:cpu_per_socket"[] [PCData (string_of_int source.s_vcpu)] ]
+          )
+        )
+      );
+
+      append virtual_hardware_section_items [
+        e "Item" [] [
+          e "rasd:Caption" [] [PCData (sprintf "%Ld MB of memory" memsize_mb)];
+          e "rasd:Description" [] [PCData "Memory Size"];
+          e "rasd:InstanceId" [] [PCData "2"];
+          e "rasd:ResourceType" [] [PCData "4"];
+          e "rasd:AllocationUnits" [] [PCData "MegaBytes"];
+          e "rasd:VirtualQuantity" [] [PCData (Int64.to_string memsize_mb)];
+        ];
+
+        e "Item" [] [
+          e "rasd:Caption" [] [PCData "USB Controller"];
+          e "rasd:InstanceId" [] [PCData "3"];
+          e "rasd:ResourceType" [] [PCData "23"];
+          e "rasd:UsbPolicy" [] [PCData "Disabled"];
+        ];
+
+        (* We always add a qxl device when outputting to RHV.
+         * See RHBZ#1213701 and RHBZ#1211231 for the reasoning
+         * behind that.
+         *)
+        e "Item" [] [
+          e "rasd:Caption" [] [PCData "Graphical Controller"];
+          e "rasd:InstanceId" [] [PCData (uuidgen ())];
+          e "rasd:ResourceType" [] [PCData "20"];
+          e "Type" [] [PCData "video"];
+          e "rasd:VirtualQuantity" [] [PCData "1"];
+          e "rasd:Device" [] [PCData "qxl"];
         ]
       ];
 
+      push_back content_subnodes (
+        e "Section" ["xsi:type", "ovf:VirtualHardwareSection_Type"]
+          !virtual_hardware_section_items
+      );
+
       e "Content" ["ovf:id", "out"; "xsi:type", "ovf:VirtualSystem_Type"]
         !content_subnodes
     ] in
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_glance.ml b/v2v/output_glance.ml
index da44700..04a2843 100644
--- a/v2v/output_glance.ml
+++ b/v2v/output_glance.ml
@@ -109,6 +109,25 @@ object
            | x -> x (* everything else is the same in libguestfs and OpenStack*)
           )
         ] in
+        if source.s_cpu_sockets <> None || source.s_cpu_cores <> None ||
+           source.s_cpu_threads <> None then (
+          push_back properties ("hw_cpu_sockets",
+                                match source.s_cpu_sockets with
+                                | None -> "1"
+                                | Some v -> string_of_int v);
+          push_back properties ("hw_cpu_cores",
+                                match source.s_cpu_cores with
+                                | None -> "1"
+                                | Some v -> string_of_int v);
+          push_back properties ("hw_cpu_threads",
+                                match source.s_cpu_threads with
+                                | None -> "1"
+                                | Some v -> string_of_int v);
+        )
+        else (
+          push_back properties ("hw_cpu_sockets", "1");
+          push_back properties ("hw_cpu_cores", string_of_int source.s_vcpu);
+        );
         (match guestcaps.gcaps_block_bus with
          | Virtio_SCSI ->
             push_back properties ("hw_scsi_model", "virtio-scsi")
diff --git a/v2v/output_qemu.ml b/v2v/output_qemu.ml
index 84efd45..a6feeaa 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);
+        push_back a (sprintf "sockets=%d"
+                             (match source.s_cpu_sockets with
+                              | None -> 1
+                              | Some v -> v));
+        push_back a (sprintf "cores=%d"
+                             (match source.s_cpu_cores with
+                              | None -> 1
+                              | Some v -> v));
+        push_back a (sprintf "threads=%d"
+                             (match source.s_cpu_threads with
+                              | None -> 1
+                              | Some v -> 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