v2v: Implement MAC address to network/bridge mapping.

This allows specific NICs (identified by their source MAC address) to
be mapped to networks or bridges on the target.  You can use the --mac
parameter to select this mapping, eg:

 $ virt-v2v ... \
    --mac 52:54:00:d0:cf:0e:network:mgmt \
    --mac 52:54:00:d0:cf:0f:network:clientdata

The old --network and --bridge mappings can also be used but --mac
takes precedence.

Note this does not adjust MAC addresses inside the guest which is a
hard problem to solve.  For this to work you must still carry over the
MAC addresses from the source to target hypervisor as that is how most
guests identify and associate functions with specific network
interfaces.
This commit is contained in:
Richard W.M. Jones
2018-07-04 12:00:58 +01:00
parent 682d3f8b01
commit fe1a886612
10 changed files with 304 additions and 52 deletions

View File

@@ -361,6 +361,7 @@ TESTS += \
test-v2v-cdrom.sh \
test-v2v-floppy.sh \
test-v2v-in-place.sh \
test-v2v-mac.sh \
test-v2v-networks-and-bridges.sh \
test-v2v-no-copy.sh \
test-v2v-o-glance.sh \
@@ -503,6 +504,9 @@ EXTRA_DIST += \
test-v2v-in-place.sh \
test-v2v-it-vddk-io-query.sh \
test-v2v-machine-readable.sh \
test-v2v-mac-expected.xml \
test-v2v-mac.sh \
test-v2v-mac.xml \
test-v2v-networks-and-bridges-expected.xml \
test-v2v-networks-and-bridges.sh \
test-v2v-networks-and-bridges.xml \

View File

@@ -42,6 +42,9 @@ type cmdline = {
root_choice : root_choice;
}
(* Matches --mac command line parameters. *)
let mac_re = PCRE.compile ~anchored:true "([[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}:[[:xdigit:]]{2}):(network|bridge):(.*)"
let parse_cmdline () =
let compressed = ref false in
let debug_overlays = ref false in
@@ -112,6 +115,16 @@ let parse_cmdline () =
| in_, out ->
Networks.add_bridge network_map in_ out
in
let add_mac str =
if not (PCRE.matches mac_re str) then
error (f_"cannot parse --mac \"%s\" parameter") str;
let mac = PCRE.sub 1 and out = PCRE.sub 3 in
let vnet_type =
match PCRE.sub 2 with
| "network" -> Network | "bridge" -> Bridge
| _ -> assert false in
Networks.add_mac network_map mac vnet_type out
in
let no_trim_warning _ =
warning (f_"the --no-trim option has been removed and now does nothing")
@@ -196,6 +209,8 @@ let parse_cmdline () =
s_"Input transport";
[ L"in-place" ], Getopt.Set in_place,
s_"Only tune the guest in the input VM";
[ L"mac" ], Getopt.String ("mac:network|bridge:out", add_mac),
s_"Map NIC to network or bridge";
[ L"machine-readable" ], Getopt.Set machine_readable,
s_"Make output machine readable";
[ S 'n'; L"network" ], Getopt.String ("in:out", add_network),

View File

@@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
(* Network, bridge mapping. *)
(* Network, bridge and MAC address mapping. *)
open Printf
@@ -26,67 +26,90 @@ open Common_gettext.Gettext
open Types
type t = {
(* For networks we use this to map a named network, or use the
* default network if no named network exists.
(* Map specific NIC with MAC address to a network or bridge. *)
mutable macs : (vnet_type * string) StringMap.t;
(* If specific NIC mapping fails, for networks we use this to map
* a named network, or use the default network if no named
* network exists.
*)
mutable network_map : string StringMap.t;
mutable default_network : string option;
(* Same as above but for bridges. *)
(* If that fails, same as above but for bridges. *)
mutable bridge_map : string StringMap.t;
mutable default_bridge : string option;
}
let map t nic =
match nic.s_vnet_type with
| Network ->
(try
let vnet = StringMap.find nic.s_vnet t.network_map in
{ nic with
s_vnet = vnet;
s_mapping_explanation =
Some (sprintf "network mapped from %S to %S"
nic.s_vnet vnet)
}
with Not_found ->
match t.default_network with
| None -> nic (* no mapping done *)
| Some default_network ->
{ nic with
s_vnet = default_network;
s_mapping_explanation =
Some (sprintf "network mapped from %S to default %S"
nic.s_vnet default_network)
}
)
| Bridge ->
(try
let vnet = StringMap.find nic.s_vnet t.bridge_map in
{ nic with
s_vnet = vnet;
s_mapping_explanation =
Some (sprintf "bridge mapped from %S to %S"
nic.s_vnet vnet)
}
with Not_found ->
match t.default_bridge with
| None -> nic (* no mapping done *)
| Some default_bridge ->
{ nic with
s_vnet = default_bridge;
s_mapping_explanation =
Some (sprintf "bridge mapped from %S to default %S"
nic.s_vnet default_bridge)
}
)
try
let mac = match nic.s_mac with None -> raise Not_found | Some mac -> mac in
let mac = String.lowercase_ascii mac in
let vnet_type, vnet = StringMap.find mac t.macs in
{ nic with
s_vnet_type = vnet_type;
s_vnet = vnet;
s_mapping_explanation =
Some (sprintf "NIC mapped by MAC address to %s:%s"
(string_of_vnet_type vnet_type) vnet)
}
with Not_found ->
match nic.s_vnet_type with
| Network ->
(try
let vnet = StringMap.find nic.s_vnet t.network_map in
{ nic with
s_vnet = vnet;
s_mapping_explanation =
Some (sprintf "network mapped from %S to %S"
nic.s_vnet vnet)
}
with Not_found ->
match t.default_network with
| None -> nic (* no mapping done *)
| Some default_network ->
{ nic with
s_vnet = default_network;
s_mapping_explanation =
Some (sprintf "network mapped from %S to default %S"
nic.s_vnet default_network)
}
)
| Bridge ->
(try
let vnet = StringMap.find nic.s_vnet t.bridge_map in
{ nic with
s_vnet = vnet;
s_mapping_explanation =
Some (sprintf "bridge mapped from %S to %S"
nic.s_vnet vnet)
}
with Not_found ->
match t.default_bridge with
| None -> nic (* no mapping done *)
| Some default_bridge ->
{ nic with
s_vnet = default_bridge;
s_mapping_explanation =
Some (sprintf "bridge mapped from %S to default %S"
nic.s_vnet default_bridge)
}
)
let create () = {
macs = StringMap.empty;
network_map = StringMap.empty;
default_network = None;
bridge_map = StringMap.empty;
default_bridge = None
}
let add_mac t mac vnet_type vnet =
let mac = String.lowercase_ascii mac in
if StringMap.mem mac t.macs then
error (f_"duplicate --mac parameter. Duplicate mappings specified for MAC address %s.") mac;
t.macs <- StringMap.add mac (vnet_type, vnet) t.macs
let add_network t i o =
if StringMap.mem i t.network_map then
error (f_"duplicate -n/--network parameter. Duplicate mappings specified for network %s.") i;

View File

@@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
(** Network, bridge mapping. *)
(** Network, bridge and MAC address mapping. *)
type t (** The map. *)
@@ -43,9 +43,18 @@ val add_default_bridge : t -> string -> unit
Equivalent to the [--bridge out] option. *)
val add_mac : t -> string -> Types.vnet_type -> string -> unit
(** Add a MAC address mapping.
Equivalent to the [-mac MAC:<network|bridge>:out] option. *)
val map : t -> Types.source_nic -> Types.source_nic
(** Apply the mapping to the source NIC, returning the updated
NIC with possibly modified [s_vnet] field.
NIC with possibly modified [s_vnet] and [s_vnet_type] fields.
MAC address mappings take precedence, followed by network
and bridge mappings if no MAC address mapping for the NIC can
be found.
[s_mapping_explanation] is set in the output with an
informational message about what was done. *)

View File

@@ -0,0 +1,32 @@
<interface type='bridge'>
<source bridge='VM Network'/>
</interface>
<interface type='network'>
<!-- NIC mapped by MAC address to Network:nancy -->
<source network='nancy'/>
<mac address='52:54:00:01:02:03'/>
</interface>
<interface type='bridge'>
<!-- NIC mapped by MAC address to Bridge:bob -->
<source bridge='bob'/>
<mac address='52:54:00:01:02:04'/>
</interface>
<interface type='network'>
<!-- network mapped from "john" to default "default_network" -->
<source network='default_network'/>
<mac address='52:54:00:01:02:05'/>
</interface>
<interface type='network'>
<!-- network mapped from "paul" to default "default_network" -->
<source network='default_network'/>
<mac address='52:54:00:01:02:06'/>
</interface>
<interface type='network'>
<!-- network mapped from "george" to default "default_network" -->
<source network='default_network'/>
<mac address='52:54:00:01:02:07'/>
</interface>
<interface type='bridge'>
<source bridge='ringo'/>
<mac address='52:54:00:01:02:08'/>
</interface>

57
v2v/test-v2v-mac.sh Executable file
View File

@@ -0,0 +1,57 @@
#!/bin/bash -
# libguestfs virt-v2v test script
# Copyright (C) 2014-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 --mac parameter.
set -e
$TEST_FUNCTIONS
skip_if_skipped
skip_if_backend uml
skip_unless_phony_guest windows.img
libvirt_uri="test://$abs_builddir/test-v2v-mac.xml"
f=$top_builddir/test-data/phony-guests/windows.img
export VIRT_TOOLS_DATA_DIR="$top_srcdir/test-data/fake-virt-tools"
d=test-v2v-mac.d
rm -rf $d
mkdir $d
# Use --no-copy because we only care about metadata for this test.
$VG virt-v2v --debug-gc \
-i libvirt -ic "$libvirt_uri" windows \
-o local -os $d --no-copy \
--mac 52:54:00:01:02:03:network:nancy \
--mac 52:54:00:01:02:04:bridge:bob \
--network default_network
# Test the libvirt XML metadata was created.
test -f $d/windows.xml
# Extract just the network interfaces from the XML.
# Delete the network model XML because that can change depending
# on whether virtio-win is installed or not.
sed -n '/interface/,/\/interface/p' $d/windows.xml |
grep -v 'model type=' > $d/networks
# Test that the output has mapped the networks and bridges correctly.
diff -ur test-v2v-mac-expected.xml $d/networks
rm -r $d

81
v2v/test-v2v-mac.xml Normal file
View File

@@ -0,0 +1,81 @@
<!--
libguestfs virt-v2v tool
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.
-->
<node>
<domain type='test'>
<name>windows</name>
<memory>1048576</memory>
<os>
<type>hvm</type>
<boot dev='hd'/>
</os>
<devices>
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='../test-data/phony-guests/windows.img'/>
<target dev='vda' bus='virtio'/>
</disk>
<!-- standard ESX bridge -->
<interface type='bridge'>
<mac address='00:00:00:00:00:00'/>
<source bridge='VM Network'/>
<model type='e1000'/>
</interface>
<!-- various different NICs which can be remapped -->
<interface type='bridge'>
<mac address='52:54:00:01:02:03'/>
<source bridge='bob'/>
<model type='e1000'/>
</interface>
<interface type='network'>
<mac address='52:54:00:01:02:04'/>
<source network='default'/>
<model type='virtio'/>
</interface>
<interface type='network'>
<mac address='52:54:00:01:02:05'/>
<source network='john'/>
<model type='virtio'/>
</interface>
<interface type='network'>
<source network='paul'/>
<model type='virtio'/>
<mac address='52:54:00:01:02:06'/>
</interface>
<interface type='network'>
<model type='rtl8139'/>
<source network='george'/>
<mac address='52:54:00:01:02:07'/>
</interface>
<interface type='bridge'>
<mac address='52:54:00:01:02:08'/>
<model type='virtio'/>
<source bridge='ringo'/>
</interface>
</devices>
</domain>
</node>

View File

@@ -227,7 +227,7 @@ and string_of_source_removable { s_removable_type = typ;
and string_of_source_nic { s_mac = mac; s_nic_model = model; s_vnet = vnet;
s_vnet_type = typ } =
sprintf "\t%s \"%s\"%s%s"
(match typ with Bridge -> "Bridge" | Network -> "Network")
(string_of_vnet_type typ)
vnet
(match mac with
| None -> ""
@@ -236,6 +236,10 @@ and string_of_source_nic { s_mac = mac; s_nic_model = model; s_vnet = vnet;
| None -> ""
| Some model -> " [" ^ string_of_nic_model model ^ "]")
and string_of_vnet_type = function
| Bridge -> "Bridge"
| Network -> "Network"
and string_of_nic_model = function
| Source_virtio_net -> "virtio"
| Source_e1000 -> "e1000"

View File

@@ -179,6 +179,7 @@ val string_of_source : source -> string
val string_of_source_disk : source_disk -> string
val string_of_controller : s_controller -> string
val string_of_nic_model : s_nic_model -> string
val string_of_vnet_type : vnet_type -> string
val string_of_source_sound_model : source_sound_model -> string
val string_of_source_video : source_video -> string
val string_of_source_cpu_topology : source_cpu_topology -> string

View File

@@ -469,6 +469,14 @@ Note this options only applies to keys and passphrases for encrypted
devices and partitions, not for passwords used to connect to remote
servers.
=item B<--mac> aa:bb:cc:dd:ee:ffB<:network:>out
=item B<--mac> aa:bb:cc:dd:ee:ffB<:bridge:>out
Map source NIC MAC address to a network or bridge.
See L</NETWORKS AND BRIDGES> below.
=item B<--machine-readable>
This option is used to make the output more machine friendly
@@ -1140,8 +1148,8 @@ Not supported.
Guests are usually connected to one or more networks, and when
converted to the target hypervisor you usually want to reconnect those
networks at the destination. The options I<--network> and I<--bridge>
allow you to do that.
networks at the destination. The options I<--network>, I<--bridge>
and I<--mac> allow you to do that.
If you are unsure of what networks and bridges are in use on the
source hypervisor, then you can examine the source metadata (libvirt
@@ -1165,8 +1173,8 @@ named external network on the source hypervisor, for example:
NICs:
Bridge "br0"
To map a specific bridge to a target network, for example C<br0> on
the source to C<ovirtmgmt> on the target, use:
To map a specific source bridge to a target network, for example
C<br0> on the source to C<ovirtmgmt> on the target, use:
virt-v2v [...] --bridge br0:ovirtmgmt
@@ -1174,6 +1182,24 @@ To map every bridge to a target network, use:
virt-v2v [...] --bridge ovirtmgmt
=head2 Fine-grained mapping of guest NICs
The I<--mac> option gives you more control over the mapping, letting
you map single NICs to either networks or bridges on the target. For
example a source guest with two NICs could map them individually to
two networks called C<mgmt> and C<clientdata> like this:
$ virt-v2v [...] \
--mac 52:54:00:d0:cf:0e:network:mgmt \
--mac 52:54:00:d0:cf:0f:network:clientdata
Note that virt-v2v does not have the ability to change a guests MAC
address. The MAC address is part of the guest metadata and must
remain the same on source and target hypervisors. Most guests will
use the MAC address to set up persistent associations between NICs and
internal names (like C<eth0>), with firewall settings, or even for
other purposes like software licensing.
=head1 INPUT FROM VMWARE VCENTER SERVER
Virt-v2v is able to import guests from VMware vCenter Server.