v2v: Add -o openstack target, writes to OpenStack & Cinder using APIs.

This commit is contained in:
Richard W.M. Jones
2018-07-26 11:33:48 +01:00
parent e4f4ed6188
commit 969cacead9
7 changed files with 784 additions and 38 deletions

9
TODO
View File

@@ -597,3 +597,12 @@ virt-v2v -o rhv-upload
That can't be fixed from the v2v side.
* There are unresolved issues about how to clean up disks on failure.
virt-v2v -o openstack
---------------------
Use the metadata service to find the -oo server-id setting. It would
no longer need to be specified on the command line. Note there are
two variations of metadata service in OpenStack, either the config
disk or link-local network address. We would need to support both, or
the possibility that there is no metadata service.

View File

@@ -70,6 +70,7 @@ SOURCES_MLI = \
output_libvirt.mli \
output_local.mli \
output_null.mli \
output_openstack.mli \
output_qemu.mli \
output_rhv.mli \
output_rhv_upload.mli \
@@ -138,6 +139,7 @@ SOURCES_ML = \
output_rhv_upload_precheck_source.ml \
output_rhv_upload.ml \
output_vdsm.ml \
output_openstack.ml \
inspect_source.ml \
target_bus_assignment.ml \
measure_disk.ml \
@@ -373,6 +375,7 @@ TESTS += \
test-v2v-o-glance.sh \
test-v2v-o-libvirt.sh \
test-v2v-o-null.sh \
test-v2v-o-openstack.sh \
test-v2v-o-qemu.sh \
test-v2v-o-rhv.sh \
test-v2v-o-vdsm-options.sh \
@@ -521,6 +524,7 @@ EXTRA_DIST += \
test-v2v-o-glance.sh \
test-v2v-o-libvirt.sh \
test-v2v-o-null.sh \
test-v2v-o-openstack.sh \
test-v2v-o-qemu.sh \
test-v2v-o-rhv.ovf.expected \
test-v2v-o-rhv.sh \

View File

@@ -138,6 +138,7 @@ let parse_cmdline () =
| "libvirt" -> output_mode := `Libvirt
| "disk" | "local" -> output_mode := `Local
| "null" -> output_mode := `Null
| "openstack" | "osp" | "rhosp" -> output_mode := `Openstack
| "ovirt" | "rhv" | "rhev" -> output_mode := `RHV
| "ovirt-upload" | "ovirt_upload" | "rhv-upload" | "rhv_upload" ->
output_mode := `RHV_Upload
@@ -412,6 +413,18 @@ read the man page virt-v2v(1).
| `Null -> no_options (); `Null
| `RHV -> no_options (); `RHV
| `QEmu -> no_options (); `QEmu
| `Openstack ->
if is_query then (
Output_openstack.print_output_options ();
exit 0
)
else (
let os_options =
Output_openstack.parse_output_options output_options in
`Openstack os_options
)
| `RHV_Upload ->
if is_query then (
Output_rhv_upload.print_output_options ();
@@ -422,6 +435,7 @@ read the man page virt-v2v(1).
Output_rhv_upload.parse_output_options output_options in
`RHV_Upload rhv_options
)
| `VDSM ->
if is_query then (
Output_vdsm.print_output_options ();
@@ -578,6 +592,18 @@ read the man page virt-v2v(1).
Output_qemu.output_qemu os qemu_boot,
output_format, output_alloc
| `Openstack os_options ->
if output_alloc <> Sparse then
error_option_cannot_be_used_in_output_mode "openstack" "-oa";
if output_format <> None then
error_option_cannot_be_used_in_output_mode "openstack" "-of";
if qemu_boot then
error_option_cannot_be_used_in_output_mode "openstack" "--qemu-boot";
Output_openstack.output_openstack output_conn output_password
output_storage os_options,
(* Force output format to raw sparse in -o openstack mode. *)
Some "raw", Sparse
| `RHV ->
if output_password <> None then
error_option_cannot_be_used_in_output_mode "rhv" "-op";

474
v2v/output_openstack.ml Normal file
View File

@@ -0,0 +1,474 @@
(* virt-v2v
* Copyright (C) 2009-2018 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.
*)
open Printf
open Unix
open Std_utils
open Tools_utils
open Unix_utils
open JSON_parser
open Common_gettext.Gettext
open Types
open Utils
(* Timeout waiting for new Cinder volumes to move to "available" state.
* We assume this could be quite a long time on backends which want
* to preallocate the storage.
*)
let available_timeout = 300 (* seconds *)
(* Timeout waiting for Cinder volumes to attach to the appliance. *)
let attach_timeout = 60 (* seconds *)
(* The -oo options supported by this output method. *)
type os_options = {
(* The server name or UUID of the conversion appliance where
* virt-v2v is currently running. In future we may be able
* to make this optional and derive it from the OpenStack
* metadata service instead.
*)
server_id : string;
(* All other OpenStack parameters, passed through unmodified
* on the openstack command line.
*)
authentication : string list;
(* Optional guest_id which, if present, is saved as
* Cinder volume property virt_v2v_guest_id on every disk
* associated with this guest.
*)
guest_id : string option;
(* This setting is used by the test suite. *)
dev_disk_by_id : string option;
}
let print_output_options () =
printf (f_"virt-v2v -oo server-id=<NAME|UUID> [os-*=...]
Specify the name or UUID of the conversion appliance using
virt-v2v ... -o openstack -oo server-id=<NAME|UUID>
When virt-v2v runs it will attach the Cinder volumes to the
conversion appliance, so this name or UUID must be the name
of the virtual machine on OpenStack where virt-v2v is running.
In addition, all usual OpenStack “os-*” parameters or “OS_*”
environment variables can be used.
Openstack “--os-*” parameters must be written as “virt-v2v -oo os-*”.
For example:
virt-v2v -oo os-username=<NAME>
equivalent to openstack: --os-username=<NAME>
or the environment variable: OS_USERNAME=<NAME>
virt-v2v -oo os-project-name=<NAME>
equivalent to openstack: --os-project-name=<NAME>
or the environment variable: OS_PROJECT_NAME=<NAME>
The os-* parameters and environment variables are optional.
")
let parse_output_options options =
let server_id = ref None in
let dev_disk_by_id = ref None in
let guest_id = ref None in
let authentication = ref [] in
List.iter (
function
| "server-id", v ->
server_id := Some v
| "dev-disk-by-id", v ->
dev_disk_by_id := Some v
| "guest-id", v ->
guest_id := Some v
| k, v ->
(* Accumulate any remaining/unknown -oo parameters
* into the authentication list, where they will be
* pass unmodified through to the openstack command.
*)
let opt = sprintf "--%s=%s" k v in
authentication := opt :: !authentication
) options;
let server_id =
match !server_id with
| None ->
error (f_"openstack: -oo server-id=<NAME|UUID> not present");
| Some server_id -> server_id in
let authentication = List.rev !authentication in
let guest_id = !guest_id in
let dev_disk_by_id = !dev_disk_by_id in
{ server_id; authentication; guest_id; dev_disk_by_id }
(* UTC conversion time. *)
let iso_time =
let time = time () in
let tm = gmtime time in
sprintf "%04d/%02d/%02d %02d:%02d:%02d"
(tm.tm_year + 1900) (tm.tm_mon + 1) tm.tm_mday
tm.tm_hour tm.tm_min tm.tm_sec
class output_openstack output_conn output_password output_storage
(os_options : os_options) =
(* The extra command line parameters derived from -oo etc. *)
let extra_args =
let args = ref os_options.authentication in
Option.may (fun oc -> List.push_back args (sprintf "--os-auth-url=%s" oc))
output_conn;
!args in
(* We use this convenient wrapper around [Tools_utils.run_command]
* for two reasons: (1) Because we want to run openstack with
* extra_args. (2) OpenStack commands are noisy so we want to
* direct stdout to /dev/null unless we're in verbose mode.
*)
let run_openstack_command args =
let cmd = [ "openstack" ] @ extra_args @ args in
let stdout_fd =
if verbose () then None
else Some (openfile "/dev/null" [O_WRONLY] 0) in
(* Note that run_command will close stdout_fd if defined. *)
Tools_utils.run_command ?stdout_fd cmd
in
(* Similar to above, run the openstack command and capture the
* JSON document printed by the command. Note you must add
* '-f json' to the args yourself.
*)
let run_openstack_command_capture_json args =
let cmd = [ "openstack" ] @ extra_args @ args in
let json, chan = Filename.open_temp_file "v2vopenstack" ".json" in
unlink_on_exit json;
let fd = descr_of_out_channel chan in
(* Note that Tools_utils.run_command closes fd. *)
if Tools_utils.run_command ~stdout_fd:fd cmd <> 0 then
None
else (
let json = json_parser_tree_parse_file json in
debug "openstack: JSON parsed as: %s"
(JSON.string_of_doc ~fmt:JSON.Indented ["", json]);
Some json
)
in
(* Create a new Cinder volume and wait for its status to change to
* "available". Returns the volume id.
*)
let create_cinder_volume name description size =
(* Cinder volumes are allocated in increments of 1 GB. Weird. *)
let size_gb =
let s = roundup64 size 1073741824L in
let s = s /^ 1073741824L in
Int64.to_string s in
let args = ref [] in
List.push_back_list args [ "volume"; "create";
"-f"; "json";
"--size"; size_gb;
"--description"; description;
"--non-bootable";
"--read-write" ];
Option.may (
fun os ->
List.push_back_list args [ "--type"; os ]
) output_storage;
List.push_back args name;
let json =
match run_openstack_command_capture_json !args with
| None ->
error (f_"openstack: failed to create a cinder volume, see earlier error messages")
| Some json -> json in
let id = object_get_string "id" json in
(* Wait for the volume state to change to "available". *)
let args = [ "volume"; "show"; "-f"; "json"; id ] in
with_timeout
(s_"wait for cinder volume status to change to \"available\"")
available_timeout
(fun () ->
match run_openstack_command_capture_json args with
| None ->
error (f_"openstack: failed to query cinder volume status, see earlier error messages")
| Some json ->
match object_get_string "status" json with
| "creating" -> None
| "available" -> Some () (* done *)
| status ->
error (f_"openstack: unknown volume status \"%s\": expected \"creating\" or \"available\"") status
);
id
in
(* Delete a cinder volume.
*
* This ignores errors since the only time we are doing this is on
* the failure path.
*)
let delete_cinder_volume id =
let args = [ "volume"; "delete"; id ] in
ignore (run_openstack_command args)
in
(* Update metadata on a cinder volume. *)
let update_cinder_volume_metadata ?bootable ?description
?(image_properties = [])
?(volume_properties = [])
id =
let args = ref [ "volume"; "set" ] in
Option.may (
fun bootable ->
List.push_back args
(if bootable then "--bootable" else "--non-bootable")
) bootable;
Option.may (
fun description ->
List.push_back_list args ["--description"; description]
) description;
let image_properties =
List.flatten (
List.map (
fun (k, v) -> [ "--image-property"; sprintf "%s=%s" k v ]
) image_properties
) in
List.push_back_list args image_properties;
let volume_properties =
List.flatten (
List.map (
fun (k, v) -> [ "--property"; sprintf "%s=%s" k v ]
) volume_properties
) in
List.push_back_list args volume_properties;
List.push_back args id;
if run_openstack_command !args <> 0 then
error (f_"openstack: failed to set image properties on cinder volume, see earlier error messages")
in
(* Attach volume to current VM and wait for it to appear.
* Returns the block device name.
*)
let attach_volume id =
let args = [ "server"; "add"; "volume";
os_options.server_id; id ] in
if run_openstack_command args <> 0 then
error (f_"openstack: failed to attach cinder volume to VM, see earlier error messages");
(* We expect the disk to appear under /dev/disk/by-id.
*
* In theory the serial number of the disk should be the
* volume ID. However the practical reality is:
*
* (1) Only the first 20 characters are included by OpenStack.
* (2) udev(?) adds extra stuff
*
* So look for any file under /dev/disk/by-id which contains
* the prefix of the volume ID as a substring.
*)
let dev_disk_by_id =
Option.default "/dev/disk/by-id" os_options.dev_disk_by_id in
let prefix_len = 16 (* maybe 20, but be safe *) in
let prefix_id =
if String.length id > prefix_len then String.sub id 0 prefix_len
else id in
with_timeout
(sprintf (f_"waiting for cinder volume %s to attach to the conversion appliance") id)
attach_timeout
(fun () ->
let entries =
try Sys.readdir dev_disk_by_id
(* It's possible for /dev/disk/by-id to not exist, since it's
* only created by udev on demand, so ignore this error.
*)
with Sys_error _ -> [||] in
let entries = Array.to_list entries in
let entries =
List.filter (fun e -> String.find e prefix_id >= 0) entries in
match entries with
| d :: _ -> Some (dev_disk_by_id // d)
| [] -> None
);
in
(* Detach volume from current VM. This does not wait and doesn't
* check for errors, since either we're on the failure path and/or
* there's nothing we could do with the error anyway.
*)
let detach_volume id =
let args = [ "server"; "remove"; "volume";
os_options.server_id; id ] in
ignore (run_openstack_command args)
in
object
inherit output
method precheck () =
(* Run the openstack command simply to check we can connect
* with the provided authentication parameters/environment
* variables. Issuing a token should have only a tiny
* overhead.
*)
let args = [ "token"; "issue" ] in
if run_openstack_command args <> 0 then
error (f_"openstack: precheck failed, there may be a problem with authentication, see earlier error messages")
method as_options =
"-o openstack" ^
(match output_conn with
| None -> ""
| Some oc -> " -oc " ^ oc) ^
(match output_password with
| None -> ""
| Some op -> " -op " ^ op)
method supported_firmware = [ TargetBIOS ]
(* List of Cinder volume IDs. *)
val mutable volume_ids = []
(* If we didn't finish successfully, delete on exit. *)
val mutable delete_volumes_on_exit = true
(* Create the Cinder volumes, wait for them to attach to the
* appliance, and return the paths of the /dev devices.
*)
method prepare_targets source overlays
target_buses guestcaps inspect target_firmware =
(* Set up an at-exit handler so we:
* (1) Unconditionally detach volumes.
* (2) Delete the volumes, but only if conversion was not successful.
*)
at_exit (
fun () ->
List.iter detach_volume volume_ids;
if delete_volumes_on_exit then (
(* XXX We probably need to wait for the previous
* detach operation to complete - unclear how.
*)
List.iter delete_cinder_volume volume_ids;
volume_ids <- []
)
);
(* Set a known description for volumes, then change it later
* when conversion is successful. In theory this would allow
* some kind of garbage collection for unfinished conversions
* in the case that virt-v2v crashes.
*)
let description =
sprintf "virt-v2v temporary volume for %s" source.s_name in
(* Create the Cinder volumes. *)
List.iter (
fun (_, ov) ->
(* Unclear what we should set the name to, so just make
* something related to the guest name. Cinder volume
* names do not need to be unique.
*)
let name = sprintf "%s-%s" source.s_name ov.ov_sd in
(* Create the cinder volume and add the returned volume
* ID to the volume_ids list.
*)
let id = create_cinder_volume name description ov.ov_virtual_size in
volume_ids <- volume_ids @ [id]
) overlays;
(* Attach volume IDs to the conversion appliance and wait
* for the device nodes to appear.
*)
List.map (
fun id ->
let dev = attach_volume id in
TargetFile dev
) volume_ids
method create_metadata source targets
target_buses guestcaps inspect target_firmware =
let nr_disks = List.length targets in
assert (nr_disks = List.length volume_ids);
assert (nr_disks >= 1);
(* Image properties are only set on the first disk.
*
* In addition we set the first disk to bootable
* (XXX see RHBZ#1308535 for why this is wrong).
*)
let image_properties =
Openstack_image_properties.create source target_buses
guestcaps inspect target_firmware in
update_cinder_volume_metadata ~bootable:true ~image_properties
(List.hd volume_ids);
(* For all disks we update the description to a "non-temporary"
* description (see above) and set volume properties.
*)
List.iteri (
fun i id ->
let description =
sprintf "%s disk %d/%d converted by virt-v2v"
source.s_name (i+1) nr_disks in
let volume_properties = ref [
"virt_v2v_version", Guestfs_config.package_version_full;
"virt_v2v_conversion_date", iso_time;
"virt_v2v_guest_name", source.s_name;
"virt_v2v_disk_index", sprintf "%d/%d" (i+1) nr_disks;
] in
(match source.s_genid with
| None -> ()
| Some genid ->
List.push_back volume_properties
("virt_v2v_vm_generation_id", genid)
);
(match os_options.guest_id with
| None -> ()
| Some guest_id ->
List.push_back volume_properties
("virt_v2v_guest_id", guest_id)
);
let volume_properties = !volume_properties in
update_cinder_volume_metadata ~description ~volume_properties id
) volume_ids;
(* Successful so don't delete on exit. *)
delete_volumes_on_exit <- false
end
let output_openstack = new output_openstack
let () = Modules_list.register_output_module "openstack"

32
v2v/output_openstack.mli Normal file
View File

@@ -0,0 +1,32 @@
(* virt-v2v
* Copyright (C) 2009-2018 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.
*)
(** [-o openstack] target. *)
type os_options
(** Miscellaneous extra command line parameters used by Openstack. *)
val print_output_options : unit -> unit
val parse_output_options : (string * string) list -> os_options
(** Print and parse openstack -oo options. *)
val output_openstack : string option -> string option -> string option ->
os_options -> Types.output
(** [output_openstack output_conn output_password output_storage os_options]
creates and returns a new {!Types.output} object specialized for writing
output to Openstack using the Openstack APIs. *)

69
v2v/test-v2v-o-openstack.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/bin/bash -
# libguestfs virt-v2v test script
# Copyright (C) 2018 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.
# Test -o openstack.
set -e
$TEST_FUNCTIONS
skip_if_skipped
skip_if_backend uml
skip_unless_phony_guest windows.img
libvirt_uri="test://$abs_top_builddir/test-data/phony-guests/guests.xml"
windows=$top_builddir/test-data/phony-guests/windows.img
export VIRT_TOOLS_DATA_DIR="$top_srcdir/test-data/fake-virt-tools"
d=test-v2v-o-openstack.d
rm -rf $d
mkdir $d
# We don't want to upload to the real openstack, so introduce a fake
# openstack binary which just logs the command line and provides
# JSON output where required.
cat > $d/openstack <<'EOF'
#!/bin/bash -
echo "$@" >> test-v2v-o-openstack.d/log
echo "$@" | grep -sq -- "-f json" && \
echo '{ "id": "dummy-vol-id", "status": "available" }'
exit 0
EOF
chmod +x $d/openstack
export PATH=$(pwd)/$d:$PATH
# Create the dummy output volume which virt-v2v will write to.
touch $d/dummy-vol-id
# Run virt-v2v -o openstack.
$VG virt-v2v --debug-gc \
-i libvirt -ic "$libvirt_uri" windows \
-o openstack -on test \
-oo server-id=test \
-oo guest-id=guestid \
-oo dev-disk-by-id=$d
# Check the log of openstack commands to make sure they look reasonable.
grep 'token issue' $d/log
grep 'volume create.*size 1.*temporary volume.*test-sda' $d/log
grep 'server add volume' $d/log
grep 'volume set.*--bootable.*dummy-vol-id' $d/log
grep 'volume set.*--property.*virt_v2v_guest_id=guestid' $d/log
grep 'server remove volume' $d/log
rm -r $d

View File

@@ -10,7 +10,7 @@ virt-v2v - Convert a guest to use KVM
virt-v2v -i disk disk.img -o local -os /var/tmp
virt-v2v -i disk disk.img -o glance
virt-v2v -i disk disk.img -o openstack -oo server-id=v2v-vm
virt-v2v -ic vpx://vcenter.example.com/Datacenter/esxi vmware_guest \
-o rhv-upload -oc https://ovirt-engine.example.com/ovirt-engine/api \
@@ -85,14 +85,15 @@ as root in this case.
For more information about converting from VMX files see
L</INPUT FROM VMWARE VMX> below.
=head2 Convert disk image to OpenStack glance
=head2 Convert disk image to OpenStack
Given a disk image from another hypervisor that you want to convert to
run on OpenStack (only KVM-based OpenStack is supported), you can do:
run on OpenStack (only KVM-based OpenStack is supported), you can run
virt-v2v inside an OpenStack VM (called C<v2v-vm> below), and do:
virt-v2v -i disk disk.img -o glance
virt-v2v -i disk disk.img -o openstack -oo server-id=v2v-vm
See L</OUTPUT TO GLANCE> below.
See L</OUTPUT TO OPENSTACK> below.
=head2 Convert disk image to disk image
@@ -123,13 +124,14 @@ qemu, do:
=head1 INPUT AND OUTPUT MODES
┌────────────┐ ┌─────────▶ -o null
-i disk ────────────┐ │ │ ─┘┌───────▶ -o local
-i disk ────────────┐ │ │ ─┘┌───────▶ -o local
-i ova ──────────┐ └──▶ │ virt-v2v │ ──┘┌───────▶ -o qemu
└────▶ │ conversion │ ───┘┌────────────┐
VMware─▶┌────────────┐ │ server │ ────▶ -o libvirt │─▶ KVM
Xen ───▶│ -i libvirt ──▶ │ │ │ (default) │
... ───▶│ (default) │ │ │ ──┐ └────────────┘
└────────────┘ │ │ ─┐└──────▶ -o glance
... ───▶│ (default) │ │ │ ──┐└────────────┘
└────────────┘ │ │ ─┐└──────▶ -o glance
│ │ ─┐└────────▶ -o openstack
-i libvirtxml ─────────▶ │ │ ┐├─────────▶ -o rhv
-i vmx ────────────────▶ │ │ │└─────────▶ -o vdsm
└────────────┘ └──────────▶ -o rhv-upload
@@ -153,7 +155,8 @@ I<-i ova> is used for reading from a VMware ova source file.
I<-i vmx> is used for reading from a VMware vmx file.
I<-o glance> is used for writing to OpenStack Glance.
I<-o openstack> is used for writing to OpenStack. I<-o glance> is a
legacy option used for writing to OpenStack Glance.
I<-o libvirt> is used for writing to any libvirt target. Libvirt can
connect to local or remote KVM hypervisors. The I<-oc> option selects
@@ -222,7 +225,7 @@ QEMU and KVM only.
=over 4
=item OpenStack Glance
=item OpenStack
=item Red Hat Virtualization (RHV) 4.1 and up
@@ -528,6 +531,9 @@ This is the same as I<-o local>.
=item B<-o> B<glance>
This is a legacy option. You should probably use I<-o openstack>
instead.
Set the output method to OpenStack Glance. In this mode the converted
guest is uploaded to Glance. See L</OUTPUT TO GLANCE> below.
@@ -566,6 +572,10 @@ The guest is converted and copied (unless you also specify
I<--no-copy>), but the results are thrown away and no metadata is
written.
=item B<-o> B<openstack>
Set the output method to OpenStack. See L</OUTPUT TO OPENSTACK> below.
=item B<-o> B<ovirt>
This is the same as I<-o rhv>.
@@ -651,6 +661,18 @@ To display short help on what options are available you can use:
virt-v2v -o rhv-upload -oo "?"
=item B<-oo guest-id=>C<ID>
For I<-o openstack> (L</OUTPUT TO OPENSTACK>) only, set a guest ID
which is saved on each Cinder volume in the C<virt_v2v_guest_id>
volume property.
=item B<-oo os->*B<=>*
For I<-o openstack> (L</OUTPUT TO OPENSTACK>) only, set optional
OpenStack authentication. For example I<-oo os-username=>NAME is
equivalent to C<openstack --os-username=NAME>.
=item B<-oo rhv-cafile=>F<ca.pem>
For I<-o rhv-upload> (L</OUTPUT TO RHV>) only, the F<ca.pem> file
@@ -677,6 +699,11 @@ For I<-o rhv-upload> (L</OUTPUT TO RHV>) only, verify the oVirt/RHV
servers identity by checking the servers certificate against the
Certificate Authority.
=item B<-oo server-id=>C<NAME|UUID>
For I<-o openstack> (L</OUTPUT TO OPENSTACK>) only, set the name
of the conversion appliance where virt-v2v is running.
=item B<-oo vdsm-compat=0.10>
=item B<-oo vdsm-compat=1.1>
@@ -779,6 +806,8 @@ directory must exist.
For I<-o rhv-upload>, this is the name of the destination Storage
Domain.
For I<-o openstack>, this is the optional Cinder volume type.
For I<-o rhv>, this can be an NFS path of the Export Storage Domain
of the form C<E<lt>hostE<gt>:E<lt>pathE<gt>>, eg:
@@ -2152,8 +2181,133 @@ enough like a RHV-M Export Storage Domain to trick virt-v2v:
touch /tmp/rhv/$uuid/dom_md
virt-v2v [...] -o rhv -os /tmp/rhv
=head1 OUTPUT TO OPENSTACK
To output to OpenStack, use the I<-o openstack> option.
=head2 OPENSTACK: SETTING UP A CONVERSION APPLIANCE
When virt-v2v is converting to OpenStack, it is unusual in that
virt-v2v B<must> be running inside a virtual machine running on top of
the OpenStack overcloud. This virtual machine is called the
"conversion appliance". Note this virtual machine is unrelated to the
guest which is being converted.
The reason for this is because to create Cinder volumes that will
contain the guest data (for the converted guest) we must attach those
Cinder volumes to an OpenStack virtual machine.
When virt-v2v is running in the conversion appliance, you must supply
the name or UUID of the conversion appliance to virt-v2v, eg:
$ openstack server list
+--------------------------------------+-----------+--------+
| ID | Name | Status |
+--------------------------------------+-----------+--------+
| bbb0147a-44b9-4d19-9a9d-10ca9a984744 | test1 | ACTIVE |
+--------------------------------------+-----------+--------+
# virt-v2v [...] \
-o openstack -oo server-id=bbb0147a-44b9-4d19-9a9d-10ca9a984744
or:
# virt-v2v [...] -o openstack -oo server-id=test1
You can run many parallel conversions inside a single conversion
appliance if you want, subject to having enough resources available.
However OpenStack itself imposes a limit that you should be aware of:
OpenStack cannot attach more than around 25 disks [the exact number
varies with configuration] to a single appliance, and that limits the
number of guests which can be converted in parallel, because each
guest's disk must be attached to the appliance while being copied.
=head2 OPENSTACK: AUTHENTICATION
Converting to OpenStack requires access to the tenant (non-admin) API
endpoints. You will need to either set up your C<$OS_*> environment
variables or use output options on the virt-v2v command line to
authenticate with OpenStack.
Normally there is a file called something like C<stackrc>,
C<overcloudrc> etc which you can simply C<source> to set everything up.
For example:
export OS_USERNAME=admin
or:
virt-v2v [...] -o openstack -oo os-username=admin
are equivalent, and have the same effect as using I<--os-username> on
the command line of OpenStack tools.
=head2 OPENSTACK: RUNNING AS ROOT
Because virt-v2v must access Cinder volumes which are presented as
F</dev> devices to the conversion appliance, virt-v2v must usually run
as root in I<-o openstack> mode.
If you use C<sudo> to start virt-v2v and you are using environment
variables for authentication, remember to use the C<sudo -E> option to
preserve the environment.
=head2 OPENSTACK: GUEST ID
virt-v2v [...] -o openstack -oo guest-id=123-456-7890
You may optionally specify I<-oo guest-id=...> on the command line.
The ID (which can be any string) is saved on each Cinder volume in the
C<virt_v2v_guest_id> volume property.
This can be used to find disks associated with a guest, or to
associate which disks are related to which guests when converting many
guests.
=head2 OPENSTACK: CONVERTING A GUEST
The final command to convert the guest, running as root, will be:
# virt-v2v [-i options ...] \
-o openstack -oo server-id=NAME|UUID [-oo guest-id=ID]
If you include authentication options on the command line then:
# virt-v2v [-i options ...] \
-o openstack -oo server-id=NAME|UUID -oo os-username=admin [etc]
=head2 OPENSTACK: BOOTING THE GUEST
Guests are converted as Cinder volume(s) (one volume per disk in the
original guest). To boot them use the
C<openstack server create --volume> option:
$ openstack volume list
+--------------------------------------+---------------+-----------+
| ID | Name | Status |
+--------------------------------------+---------------+-----------+
| c4d06d15-22ef-462e-9eff-ab54ab285a1f | fedora-27-sda | available |
+--------------------------------------+---------------+-----------+
$ openstack server create \
--flavor x1.small \
--volume c4d06d15-22ef-462e-9eff-ab54ab285a1f \
myguest
$ openstack console url show myguest
=head2 OPENSTACK: OTHER CONVERSION OPTIONS
To specify the Cinder volume type, use I<-os>. If not specified then
no Cinder volume type is used.
The following options are B<not> supported with OpenStack: I<-oa>,
I<-of>.
=head1 OUTPUT TO GLANCE
Note this is a legacy option. In most cases you should use
L</OUTPUT TO OPENSTACK> instead.
To output to OpenStack Glance, use the I<-o glance> option.
This runs the L<glance(1)> CLI program which must be installed on the
@@ -2184,34 +2338,7 @@ Glance disks either. If the guest has multiple disks, then the first
(assumed to be the system disk) will have the name of the guest, and
the second and subsequent data disks will be called
C<I<guestname>-disk2>, C<I<guestname>-disk3> etc. It may be best to
leave the system disk in Glance, and import the data disks to Cinder
(see next section).
=head2 Importing disks into Cinder
Since most virt-v2v guests are "pets", Glance is perhaps not the best
place to store them. There is no way for virt-v2v to upload directly
to Cinder (L<https://bugzilla.redhat.com/1155229>). There are two
ways to upload to Cinder:
=over 4
=item 1.
Import the image to Glance first (ie. I<-o glance>) and then copy it
to Cinder:
cinder create --image-id <GLANCE-IMAGE-UUID> <SIZE>
=item 2.
Create (through some other means) a new volume / LUN in your Cinder
backing store. Migrate the guest to this volume (using I<-o local>).
Then ask Cinder to take over management of the volume using:
cinder manage <VOLUMEREF>
=back
leave the system disk in Glance, and import the data disks to Cinder.
=head1 RESOURCE REQUIREMENTS
@@ -2414,6 +2541,11 @@ see L<http://libvirt.org/auth.html>. Alternatively, use
I<-oc qemu:///session>, which will write to your per-user libvirt
instance.
=item Writing to Openstack
Because of how Cinder volumes are presented as F</dev> block devices,
using I<-o openstack> normally requires that virt-v2v is run as root.
=item Writing to Glance
This does I<not> need root (in fact it probably wont work), but may