The target VM will have several buses to which disks can be attached.
Commonly it will have an IDE bus, and possibly a virtio-blk "bus" (not
really a bus) and/or a SCSI bus.
Virt-v2v does not model this at the moment. Disks are just added to
the output XML in the order that we write them, and so they can move
around with respect to the target VM.
This commit introduces the idea that we should model the target disk
buses, and we should try to assign fixed and removable disks to slots
on each of those buses. In this commit, the modelling is not used by
any output mode, but that will be fixed in the next commit.
---
v2v/types.ml | 29 +++++++++++++++++++++++
v2v/types.mli | 14 +++++++++++
v2v/v2v.ml | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 119 insertions(+)
diff --git a/v2v/types.ml b/v2v/types.ml
index 34e169c..522814e 100644
--- a/v2v/types.ml
+++ b/v2v/types.ml
@@ -350,6 +350,35 @@ gcaps_acpi = %b
gcaps.gcaps_arch
gcaps.gcaps_acpi
+type target_buses = {
+ target_virtio_blk_bus : target_bus_slot array;
+ target_ide_bus : target_bus_slot array;
+ target_scsi_bus : target_bus_slot array;
+}
+
+and target_bus_slot =
+ | BusSlotEmpty
+ | BusSlotTarget of target
+ | BusSlotRemovable of source_removable
+
+let string_of_target_bus_slots bus_name slots =
+ let slots =
+ Array.mapi (
+ fun slot_nr slot ->
+ sprintf "%s slot %d:\n" bus_name slot_nr ^
+ (match slot with
+ | BusSlotEmpty -> "\t(slot empty)\n"
+ | BusSlotTarget t -> string_of_target t
+ | BusSlotRemovable r -> string_of_source_removable r ^ "\n"
+ )
+ ) slots in
+ String.concat "" (Array.to_list slots)
+
+let string_of_target_buses buses =
+ string_of_target_bus_slots "virtio-blk" buses.target_virtio_blk_bus ^
+ string_of_target_bus_slots "ide" buses.target_ide_bus ^
+ string_of_target_bus_slots "scsi" buses.target_scsi_bus
+
class virtual input = object
method virtual as_options : string
method virtual source : unit -> source
diff --git a/v2v/types.mli b/v2v/types.mli
index 6f9bf0d..45014a7 100644
--- a/v2v/types.mli
+++ b/v2v/types.mli
@@ -196,6 +196,20 @@ and guestcaps_video_type = QXL | Cirrus
val string_of_guestcaps : guestcaps -> string
+type target_buses = {
+ target_virtio_blk_bus : target_bus_slot array;
+ target_ide_bus : target_bus_slot array;
+ target_scsi_bus : target_bus_slot array;
+}
+(** Mapping of fixed and removable disks to buses. *)
+
+and target_bus_slot =
+| BusSlotEmpty (** This bus slot is empty. *)
+| BusSlotTarget of target (** Contains a fixed disk. *)
+| BusSlotRemovable of source_removable (** Contains a removable CD/floppy. *)
+
+val string_of_target_buses : target_buses -> string
+
class virtual input : object
method virtual as_options : string
(** Converts the input object back to the equivalent command line options.
diff --git a/v2v/v2v.ml b/v2v/v2v.ml
index 5bca846..61ef4c1 100644
--- a/v2v/v2v.ml
+++ b/v2v/v2v.ml
@@ -311,6 +311,11 @@ let rec main () =
g#shutdown ();
g#close ();
+ message (f_"Assigning disks to buses");
+ let target_buses = target_bus_assignment source targets guestcaps in
+ if verbose () then
+ printf "%s%!" (string_of_target_buses target_buses);
+
(* Force a GC here, to ensure that we're using the minimum resources
* as we go into the copy stage. The particular reason is that
* Windows conversion may have opened a second libguestfs handle
@@ -833,4 +838,75 @@ and du filename =
| line::_ -> (try Some (Int64.of_string line) with _ -> None)
| [] -> None
+(* Assign fixed and removable disks to target buses, as best we can.
+ * This is not solvable for all guests, but at least avoid overlapping
+ * disks (RHBZ#1238053). XXX This doesn't do the right thing for PC
+ * legacy floppy devices.
+ *)
+and target_bus_assignment source targets guestcaps =
+ let virtio_blk_bus = ref [| |]
+ and ide_bus = ref [| |]
+ and scsi_bus = ref [| |] in
+
+ (* Insert a slot into the bus array, making the array bigger if necessary. *)
+ let insert bus i slot =
+ let oldbus = !bus in
+ let oldlen = Array.length oldbus in
+ if i >= oldlen then (
+ bus := Array.make (i+1) BusSlotEmpty;
+ Array.blit oldbus 0 !bus 0 oldlen
+ );
+ Array.set !bus i slot
+ in
+
+ (* Insert a slot into the bus, but if the desired slot is not empty, then
+ * increment the slot number until we find an empty one. Returns
+ * true if we got the desired slot.
+ *)
+ let rec insert_after bus i slot =
+ let len = Array.length !bus in
+ if i >= len || Array.get !bus i = BusSlotEmpty then (
+ insert bus i slot; true
+ ) else (
+ ignore (insert_after bus (i+1) slot); false
+ )
+ in
+
+ (* Add the fixed disks (targets) to either the virtio-blk or IDE bus,
+ * depending on whether the guest has virtio drivers or not.
+ *)
+ iteri (
+ fun i t ->
+ let t = BusSlotTarget t in
+ match guestcaps.gcaps_block_bus with
+ | Virtio_blk -> insert virtio_blk_bus i t
+ | IDE -> insert ide_bus i t
+ ) targets;
+
+ (* Now try to add the removable disks to the bus at the same slot
+ * they originally occupied, but if the slot is occupied, emit a
+ * a warning and insert the disk in the next empty slot in that bus.
+ *)
+ List.iter (
+ fun r ->
+ let bus = match r.s_removable_controller with
+ | None -> ide_bus (* Wild guess, but should be safe. *)
+ | Some Source_virtio_blk -> virtio_blk_bus
+ | Some Source_IDE -> ide_bus
+ | Some Source_SCSI -> scsi_bus in
+ match r.s_removable_slot with
+ | None -> ignore (insert_after bus 0 (BusSlotRemovable r))
+ | Some desired_slot_nr ->
+ if not (insert_after bus desired_slot_nr (BusSlotRemovable r)) then
+ warning (f_"removable %s device in slot %d clashes with another disk, so
it has been moved to a higher numbered slot on the same bus. This may mean that this
removable device has a different name inside the guest (for example a CD-ROM originally
called /dev/hdc might move to /dev/hdd, or from D: to E: on a Windows guest).")
+ (match r.s_removable_type with
+ | CDROM -> s_"CD-ROM"
+ | Floppy -> s_"floppy disk")
+ desired_slot_nr
+ ) source.s_removables;
+
+ { target_virtio_blk_bus = !virtio_blk_bus;
+ target_ide_bus = !ide_bus;
+ target_scsi_bus = !scsi_bus }
+
let () = run_main_and_handle_errors main
--
2.3.1