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