mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-22 07:03:38 +00:00
v2v: -o qemu: Refactor generation of the qemu shell script.
This change refactors how the qemu shell script is generated, making it a bit less likely that we'll get the quoting wrong. Parameters are now added to a list, unquoted, and all quoting is done when printing out the list. Checked by running virt-v2v -o qemu before and after the change and manually comparing the output (which is not identical, but still correct).
This commit is contained in:
@@ -59,6 +59,17 @@ module Char = struct
|
||||
| 'a'..'f' -> true
|
||||
| 'A'..'F' -> true
|
||||
| _ -> false
|
||||
|
||||
let isalpha = function
|
||||
| 'a'..'z' -> true
|
||||
| 'A'..'Z' -> true
|
||||
| _ -> false
|
||||
|
||||
let isalnum = function
|
||||
| '0'..'9' -> true
|
||||
| 'a'..'z' -> true
|
||||
| 'A'..'Z' -> true
|
||||
| _ -> false
|
||||
end
|
||||
|
||||
module String = struct
|
||||
|
||||
@@ -39,6 +39,10 @@ module Char : sig
|
||||
(** Return true if the character is a digit [[0-9]]. *)
|
||||
val isxdigit : char -> bool
|
||||
(** Return true if the character is a hex digit [[0-9a-fA-F]]. *)
|
||||
val isalpha : char -> bool
|
||||
(** Return true if the character is a US ASCII 7 bit alphabetic. *)
|
||||
val isalnum : char -> bool
|
||||
(** Return true if the character is a US ASCII 7 bit alphanumeric. *)
|
||||
end
|
||||
(** Override the Char module from stdlib. *)
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ SOURCES_MLI = \
|
||||
output_vdsm.mli \
|
||||
OVF.mli \
|
||||
parse_libvirt_xml.mli \
|
||||
qemu_command.mli \
|
||||
target_bus_assignment.mli \
|
||||
types.mli \
|
||||
uefi.mli \
|
||||
@@ -79,6 +80,7 @@ SOURCES_ML = \
|
||||
input_disk.ml \
|
||||
parse_libvirt_xml.ml \
|
||||
create_libvirt_xml.ml \
|
||||
qemu_command.ml \
|
||||
input_libvirtxml.ml \
|
||||
input_libvirt_other.ml \
|
||||
input_libvirt_vcenter_https.ml \
|
||||
|
||||
@@ -69,61 +69,51 @@ object
|
||||
let machine_q35 = secure_boot_required in
|
||||
let smm = secure_boot_required in
|
||||
|
||||
let chan = open_out file in
|
||||
(* Construct the command line. Note that the [Qemu_command]
|
||||
* module deals with shell and qemu comma quoting.
|
||||
*)
|
||||
let cmd = Qemu_command.create ~arch:guestcaps.gcaps_arch () in
|
||||
let flag = Qemu_command.flag cmd
|
||||
and arg = Qemu_command.arg cmd
|
||||
and arg_noquote = Qemu_command.arg_noquote cmd
|
||||
and commas = Qemu_command.commas cmd in
|
||||
|
||||
let fpf fs = fprintf chan fs in
|
||||
let nl = " \\\n\t" in
|
||||
fpf "#!/bin/sh -\n";
|
||||
fpf "\n";
|
||||
|
||||
(match uefi_firmware with
|
||||
| None -> ()
|
||||
| Some { Uefi.vars = vars_template } ->
|
||||
fpf "# Make a copy of the UEFI variables template\n";
|
||||
fpf "uefi_vars=\"$(mktemp)\"\n";
|
||||
fpf "cp %s \"$uefi_vars\"\n" (quote vars_template);
|
||||
fpf "\n"
|
||||
);
|
||||
|
||||
fpf "qemu-system-%s" guestcaps.gcaps_arch;
|
||||
fpf "%s-no-user-config -nodefaults" nl;
|
||||
fpf "%s-name %s" nl (quote source.s_name);
|
||||
fpf "%s-machine %s%saccel=kvm:tcg" nl
|
||||
(if machine_q35 then "q35," else "")
|
||||
(if smm then "smm=on," else "");
|
||||
flag "-no-user-config"; flag "-nodefaults";
|
||||
arg "-name" source.s_name;
|
||||
commas "-machine" (if machine_q35 then ["q35"] else [] @
|
||||
if smm then ["smm=on"] else [] @
|
||||
["accel=kvm:tcg"]);
|
||||
|
||||
(match uefi_firmware with
|
||||
| None -> ()
|
||||
| Some { Uefi.code = code } ->
|
||||
if secure_boot_required then
|
||||
fpf "%s-global driver=cfi.pflash01,property=secure,value=on" nl;
|
||||
fpf "%s-drive if=pflash,format=raw,file=%s,readonly" nl (quote code);
|
||||
fpf "%s-drive if=pflash,format=raw,file=\"$uefi_vars\"" nl
|
||||
commas "-global"
|
||||
["driver=cfi.pflash01"; "property=secure"; "value=on"];
|
||||
commas "-drive"
|
||||
["if=pflash"; "format=raw"; "file=" ^ code; "readonly"];
|
||||
arg_noquote "-drive" "if=pflash,format=raw,file=\"$uefi_vars\"";
|
||||
);
|
||||
|
||||
fpf "%s-m %Ld" nl (source.s_memory /^ 1024L /^ 1024L);
|
||||
arg "-m" (Int64.to_string (source.s_memory /^ 1024L /^ 1024L));
|
||||
if source.s_vcpu > 1 then
|
||||
fpf "%s-smp %d" nl source.s_vcpu;
|
||||
arg "-smp" (string_of_int source.s_vcpu);
|
||||
|
||||
let make_disk if_name i = function
|
||||
| BusSlotEmpty -> ()
|
||||
|
||||
| BusSlotTarget t ->
|
||||
let qemu_quoted_filename = String.replace t.target_file "," ",," in
|
||||
let drive_param =
|
||||
sprintf "file=%s,format=%s,if=%s,index=%d,media=disk"
|
||||
qemu_quoted_filename t.target_format if_name i in
|
||||
fpf "%s-drive %s" nl (quote drive_param)
|
||||
commas "-drive" ["file=" ^ t.target_file; "format=" ^ t.target_format;
|
||||
"if=" ^ if_name; "index=" ^ string_of_int i;
|
||||
"media=disk"]
|
||||
|
||||
| BusSlotRemovable { s_removable_type = CDROM } ->
|
||||
let drive_param =
|
||||
sprintf "format=raw,if=%s,index=%d,media=cdrom" if_name i in
|
||||
fpf "%s-drive %s" nl (quote drive_param)
|
||||
commas "-drive" ["format=raw"; "if=" ^ if_name;
|
||||
"index=" ^ string_of_int i; "media=cdrom"]
|
||||
|
||||
| BusSlotRemovable { s_removable_type = Floppy } ->
|
||||
let drive_param =
|
||||
sprintf "format=raw,if=%s,index=%d,media=floppy" if_name i in
|
||||
fpf "%s-drive %s" nl (quote drive_param)
|
||||
commas "-drive" ["format=raw"; "if=" ^ if_name;
|
||||
"index=" ^ string_of_int i; "media=floppy"]
|
||||
in
|
||||
Array.iteri (make_disk "virtio") target_buses.target_virtio_blk_bus;
|
||||
Array.iteri (make_disk "ide") target_buses.target_ide_bus;
|
||||
@@ -132,21 +122,17 @@ object
|
||||
| BusSlotEmpty -> ()
|
||||
|
||||
| BusSlotTarget t ->
|
||||
let qemu_quoted_filename = String.replace t.target_file "," ",," in
|
||||
let drive_param =
|
||||
sprintf "file=%s,format=%s,if=scsi,bus=0,unit=%d,media=disk"
|
||||
qemu_quoted_filename t.target_format i in
|
||||
fpf "%s-drive %s" nl (quote drive_param)
|
||||
commas "-drive" ["file=" ^ t.target_file; "format=" ^ t.target_format;
|
||||
"if=scsi"; "bus=0"; "unit=" ^ string_of_int i;
|
||||
"media=disk"]
|
||||
|
||||
| BusSlotRemovable { s_removable_type = CDROM } ->
|
||||
let drive_param =
|
||||
sprintf "format=raw,if=scsi,bus=0,unit=%d,media=cdrom" i in
|
||||
fpf "%s-drive %s" nl (quote drive_param)
|
||||
commas "-drive" ["format=raw"; "if=scsi"; "bus=0";
|
||||
"unit=" ^ string_of_int i; "media=cdrom"]
|
||||
|
||||
| BusSlotRemovable { s_removable_type = Floppy } ->
|
||||
let drive_param =
|
||||
sprintf "format=raw,if=scsi,bus=0,unit=%d,media=floppy" i in
|
||||
fpf "%s-drive %s" nl (quote drive_param)
|
||||
commas "-drive" ["format=raw"; "if=scsi"; "bus=0";
|
||||
"unit=" ^ string_of_int i; "media=floppy"]
|
||||
in
|
||||
Array.iteri make_scsi target_buses.target_scsi_bus;
|
||||
|
||||
@@ -161,9 +147,12 @@ object
|
||||
| RTL8139 -> "rtl8139" in
|
||||
iteri (
|
||||
fun i nic ->
|
||||
fpf "%s-netdev user,id=net%d" nl i;
|
||||
fpf "%s-device %s,netdev=net%d%s" nl
|
||||
net_bus i (match nic.s_mac with None -> "" | Some mac -> ",mac=" ^ mac)
|
||||
commas "-netdev" ["user"; "id=net" ^ string_of_int i];
|
||||
commas "-device" [net_bus;
|
||||
sprintf "netdev=net%d%s" i
|
||||
(match nic.s_mac with
|
||||
| None -> ""
|
||||
| Some mac -> "mac=" ^ mac)]
|
||||
) source.s_nics;
|
||||
|
||||
(* Add a display. *)
|
||||
@@ -172,15 +161,18 @@ object
|
||||
| Some display ->
|
||||
(match display.s_display_type with
|
||||
| Window ->
|
||||
fpf "%s-display gtk" nl
|
||||
arg "-display" "gtk"
|
||||
| VNC ->
|
||||
fpf "%s-display vnc=:0" nl
|
||||
arg "-display" "vnc=:0"
|
||||
| Spice ->
|
||||
fpf "%s-spice port=%d,addr=127.0.0.1" nl
|
||||
(match display.s_port with None -> 5900 | Some p -> p)
|
||||
commas "-spice" [sprintf "port=%d"
|
||||
(match display.s_port with
|
||||
| None -> 5900
|
||||
| Some p -> p);
|
||||
"addr=127.0.0.1"]
|
||||
);
|
||||
fpf "%s-vga %s" nl
|
||||
(match guestcaps.gcaps_video with Cirrus -> "cirrus" | QXL -> "qxl")
|
||||
arg "-vga"
|
||||
(match guestcaps.gcaps_video with Cirrus -> "cirrus" | QXL -> "qxl")
|
||||
);
|
||||
|
||||
(* Add a sound card. *)
|
||||
@@ -189,22 +181,36 @@ object
|
||||
| Some { s_sound_model = model } ->
|
||||
if qemu_supports_sound_card model then (
|
||||
match model with
|
||||
| AC97 -> fpf "%s-device AC97" nl
|
||||
| ES1370 -> fpf "%s-device ES1370" nl
|
||||
| ICH6 -> fpf "%s-device intel-hda -device hda-duplex" nl
|
||||
| ICH9 -> fpf "%s-device ich9-intel-hda" nl
|
||||
| PCSpeaker -> fpf "%s-soundhw pcspk" nl (* not qdev-ified *)
|
||||
| SB16 -> fpf "%s-device sb16" nl
|
||||
| USBAudio -> fpf "%s-device usb-audio" nl
|
||||
| AC97 -> arg "-device" "AC97"
|
||||
| ES1370 -> arg "-device" "ES1370"
|
||||
| ICH6 -> arg "-device" "intel-hda -device hda-duplex"
|
||||
| ICH9 -> arg "-device" "ich9-intel-hda"
|
||||
| PCSpeaker -> arg "-soundhw" "pcspk" (* not qdev-ified *)
|
||||
| SB16 -> arg "-device" "sb16"
|
||||
| USBAudio -> arg "-device" "usb-audio"
|
||||
)
|
||||
);
|
||||
|
||||
(* Add a serial console to Linux guests. *)
|
||||
if inspect.i_type = "linux" then
|
||||
fpf "%s-serial stdio" nl;
|
||||
arg "-serial" "stdio";
|
||||
|
||||
(* Write the output file. *)
|
||||
let chan = open_out file in
|
||||
let fpf fs = fprintf chan fs in
|
||||
fpf "#!/bin/sh -\n";
|
||||
fpf "\n";
|
||||
|
||||
(match uefi_firmware with
|
||||
| None -> ()
|
||||
| Some { Uefi.vars = vars_template } ->
|
||||
fpf "# Make a copy of the UEFI variables template\n";
|
||||
fpf "uefi_vars=\"$(mktemp)\"\n";
|
||||
fpf "cp %s \"$uefi_vars\"\n" (quote vars_template);
|
||||
fpf "\n"
|
||||
);
|
||||
|
||||
Qemu_command.to_chan cmd chan;
|
||||
close_out chan;
|
||||
|
||||
Unix.chmod file 0o755;
|
||||
|
||||
101
v2v/qemu_command.ml
Normal file
101
v2v/qemu_command.ml
Normal file
@@ -0,0 +1,101 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2017 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.
|
||||
*)
|
||||
|
||||
(** Generate a qemu command line, dealing with quoting. *)
|
||||
|
||||
open Printf
|
||||
|
||||
open Common_utils
|
||||
|
||||
type t = {
|
||||
cmd : string;
|
||||
mutable args : arg list; (* list constructed in reverse order *)
|
||||
}
|
||||
and arg =
|
||||
| Flag of string
|
||||
| Arg of string * string * bool
|
||||
| Commas of string * string list
|
||||
|
||||
let create ?(arch = "x86_64") () =
|
||||
{ cmd = "qemu-system-" ^ arch; args = [] }
|
||||
|
||||
let flag t k =
|
||||
assert (String.is_prefix k "-");
|
||||
t.args <- Flag k :: t.args
|
||||
|
||||
let arg t k v =
|
||||
assert (String.is_prefix k "-");
|
||||
t.args <- Arg (k, v, true) :: t.args
|
||||
|
||||
let arg_noquote t k v =
|
||||
assert (String.is_prefix k "-");
|
||||
t.args <- Arg (k, v, false) :: t.args
|
||||
|
||||
let commas t k vs =
|
||||
assert (String.is_prefix k "-");
|
||||
List.iter (fun v -> assert (v <> "")) vs;
|
||||
t.args <- Commas (k, vs) :: t.args
|
||||
|
||||
let nl = " \\\n\t"
|
||||
|
||||
(* If the value contains only simple characters then it doesn't
|
||||
* need quoting. This keeps the output as similar as possible
|
||||
* to the old code.
|
||||
*)
|
||||
let do_quoting str =
|
||||
let len = String.length str in
|
||||
let ret = ref false in
|
||||
for i = 0 to len-1 do
|
||||
let c = String.unsafe_get str i in
|
||||
if not (Char.isalnum c) &&
|
||||
c <> '.' && c <> '-' && c <> '_' &&
|
||||
c <> '=' && c <> ',' && c <> ':' && c <> '/'
|
||||
then
|
||||
ret := true
|
||||
done;
|
||||
!ret
|
||||
|
||||
let print_quoted_param chan k v =
|
||||
if not (do_quoting v) then
|
||||
fprintf chan "%s%s %s" nl k v
|
||||
else
|
||||
fprintf chan "%s%s %s" nl k (quote v)
|
||||
|
||||
let to_chan t chan =
|
||||
fprintf chan "%s" t.cmd;
|
||||
List.iter (
|
||||
function
|
||||
| Flag k ->
|
||||
fprintf chan "%s%s" nl k
|
||||
| Arg (k, v, true) ->
|
||||
print_quoted_param chan k v
|
||||
| Arg (k, v, false) ->
|
||||
fprintf chan "%s%s %s" nl k v
|
||||
| Commas (k, vs) ->
|
||||
let vs = List.map (fun s -> String.replace s "," ",,") vs in
|
||||
let v = String.concat "," vs in
|
||||
print_quoted_param chan k v
|
||||
) (List.rev t.args);
|
||||
fprintf chan "\n"
|
||||
|
||||
let to_script t filename =
|
||||
let chan = open_out filename in
|
||||
fprintf chan "#!/bin/sh -\n";
|
||||
to_chan t chan;
|
||||
close_out chan;
|
||||
Unix.chmod filename 0o755
|
||||
55
v2v/qemu_command.mli
Normal file
55
v2v/qemu_command.mli
Normal file
@@ -0,0 +1,55 @@
|
||||
(* virt-v2v
|
||||
* Copyright (C) 2009-2017 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.
|
||||
*)
|
||||
|
||||
(** Generate a qemu command line, dealing with quoting. *)
|
||||
|
||||
type t
|
||||
|
||||
val create : ?arch:string -> unit -> t
|
||||
(** Create an empty qemu command. If the optional [?arch] parameter
|
||||
is supplied then the command will be [qemu-system-<arch>],
|
||||
otherwise it will be [qemu-system-x86_64]. *)
|
||||
|
||||
val flag : t -> string -> unit
|
||||
(** [flag t "-foo"] adds a parameter to the command line with no argument. *)
|
||||
|
||||
val arg : t -> string -> string -> unit
|
||||
(** [arg t "-m" "1024"] adds [-m 1024] to the command line.
|
||||
|
||||
The value will shell-quoted if required, so you do not need to quote
|
||||
the string. However if the value is a comma-separated list
|
||||
(eg. [-drive file=foo,if=ide]) then do {b not} use this function, call
|
||||
{!commas} instead. *)
|
||||
|
||||
val arg_noquote : t -> string -> string -> unit
|
||||
(** Like {!arg} except no quoting is done on the value. *)
|
||||
|
||||
val commas : t -> string -> string list -> unit
|
||||
(** [commas t "-drive" ["file=foo"; "if=ide"]] adds a comma-separated
|
||||
list of parameters to the command line [-drive file=foo,if=ide].
|
||||
|
||||
This does both qemu comma-quoting and shell-quoting as required. *)
|
||||
|
||||
val to_script : t -> string -> unit
|
||||
(** [to_script t "./file.sh"] writes the resulting command line to
|
||||
a file. The file begins with [#!/bin/sh] and is chmod 0755. *)
|
||||
|
||||
val to_chan : t -> out_channel -> unit
|
||||
(** [to_chan t chan] appends the resulting command line to
|
||||
an output channel. The caller must write [!#/bin/sh] and chmod 0755
|
||||
the output file, if needed. *)
|
||||
Reference in New Issue
Block a user