Skip to content

Commit becd5a4

Browse files
committed
add case for migrating guest with bridge interface
xxxx-299038: [bridge] migrate guest with bridge type interface Signed-off-by: nanli <[email protected]>
1 parent e002d4d commit becd5a4

File tree

2 files changed

+283
-0
lines changed

2 files changed

+283
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
- virtual_network.migrate.migrate_with_bridge_type_interface:
2+
type = migrate_with_bridge_type_interface
3+
start_vm = "no"
4+
take_regular_screendumps = no
5+
storage_type = 'nfs'
6+
setup_local_nfs = 'yes'
7+
disk_type = "file"
8+
disk_source_protocol = "netfs"
9+
nfs_mount_dir = "/var/lib/libvirt/migrate"
10+
nfs_mount_options = "soft,timeo=50,retrans=3"
11+
virsh_migrate_dest_state = running
12+
virsh_migrate_options = "--live --p2p --verbose"
13+
virsh_migrate_connect_uri = "qemu:///system"
14+
outside_ip = "www.google.com"
15+
migration_setup = "yes"
16+
iface_model = "virtio"
17+
iface_queues = "5"
18+
new_queues = "3"
19+
ssh_remote_auth = True
20+
unprivileged_user = ""
21+
mnt_path_name = ${nfs_mount_dir}
22+
nfs_server_ip = "${migrate_source_host}"
23+
migrate_desturi_port = "16509"
24+
migrate_desturi_type = "ssh"
25+
virsh_migrate_desturi = "qemu+ssh://${migrate_dest_host}/system"
26+
vm_ping_outside = "pass"
27+
expected_xpath = [{'element_attrs': ["//interface/driver[@queues='${iface_queues}']"]}]
28+
check_network_accessibility_after_mig = "yes"
29+
variants:
30+
- linux_bridge:
31+
bridge_type = "linux"
32+
iface_type = "bridge"
33+
bridge_name = "br0"
34+
iface_dict = {'source': {'bridge': '${bridge_name}'},'type_name':'${iface_type}', 'model':'${iface_model}', 'driver': {'driver_attr': {'queues': '${iface_queues}'}}}
35+
- ovs_bridge:
36+
bridge_type = "ovs"
37+
ovs_bridge_name = "ovsbr0"
38+
net_name = 'ovs-net'
39+
network_dict = {'bridge': {'name': '${ovs_bridge_name}'}, 'forward': {'mode': 'bridge'}, 'name': '${net_name}', 'virtualport_type': 'openvswitch'}
40+
iface_type = "network"
41+
iface_dict = {'type_name':'${iface_type}', 'source': {'network': '${net_name}'}, 'model':'${iface_model}', 'driver': {'driver_attr': {'queues': '${iface_queues}'}}}
42+
variants:
43+
- precopy_migration:
44+
migration_type = "precopy"
45+
virsh_migrate_extra = ""
46+
migrate_vm_back = "yes"
47+
- postcopy_migration:
48+
migration_type = "postcopy"
49+
virsh_migrate_extra = "--postcopy"
50+
- cancel_migration:
51+
migration_type = "cancel"
52+
cancel_migration = "yes"
53+
status_error = "yes"
54+
err_msg = "operation aborted: migration out job: canceled by client|operation aborted: job 'migration out' canceled by client"
55+
virsh_migrate_extra = "--bandwidth 10"
56+
action_during_mig = '[{"func": "virsh.domjobabort", "after_event": "iteration: '1'", "func_param": "'%s' % params.get('migrate_main_vm')"}]'
57+
variants:
58+
- start_with_interface:
59+
interface_timing = "start"
60+
- hotplug_interface:
61+
interface_timing = "hotplug"
62+
hotplug = "yes"
63+
attach_interface = "yes"
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2+
#
3+
# Copyright Redhat
4+
#
5+
# SPDX-License-Identifier: GPL-2.0
6+
# Author: Nannan Li<[email protected]>
7+
#
8+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9+
import aexpect.remote
10+
import re
11+
import os
12+
13+
from virttest import utils_net
14+
from virttest import virsh
15+
from virttest.libvirt_xml import vm_xml
16+
from virttest.utils_libvirt import libvirt_network
17+
from virttest.utils_libvirt import libvirt_vmxml
18+
from virttest.libvirt_xml.vm_xml import VMXML
19+
20+
from provider.guest_os_booting import guest_os_booting_base as guest_os
21+
from provider.migration import base_steps
22+
from provider.virtual_network import network_base
23+
24+
25+
def run(test, params, env):
26+
"""
27+
Test migration with bridge type interface
28+
1. Setup bridge and virtual network according to bridge type
29+
2. Migrate to target host
30+
3. Check on target host for network functions:
31+
- Guest ping outside
32+
- Check for multiqueue
33+
- Check multiqueues in VM live XML (should have <driver ... queues='5'>)
34+
4. Migrate back from dst to src
35+
36+
:param test: test object
37+
:param params: Dictionary with the test parameters
38+
:param env: Dictionary with test environment.
39+
"""
40+
41+
def check_multiqueue_in_guest(vm_session):
42+
"""
43+
Check multiqueue configuration inside the guest.
44+
45+
:params, vm_session: vm session object.
46+
"""
47+
test.log.info("Checking multiqueue configuration in guest")
48+
guest_iface_info = vm_session.cmd_output("ip --color=never l").strip()
49+
iface_matches = re.findall(
50+
r"^\d+: (\S+?)[@:].*state UP.*$", guest_iface_info, re.MULTILINE)
51+
if not iface_matches:
52+
test.fail("Failed to get network interface name in guest")
53+
iface_name = iface_matches[0]
54+
55+
_, output = vm_session.cmd_status_output("ethtool -l %s" % iface_name)
56+
test.log.debug("ethtool cmd output:%s" % output)
57+
if not re.findall("Combined:.*?%s" % iface_queues, output):
58+
test.fail("Expected Current hardware settings Combined: %d" % iface_queues)
59+
60+
test.log.info("Setting combined queues to 3 for %s", iface_name)
61+
utils_net.set_channel(vm_session, iface_name, "combined", new_queues)
62+
_, output = vm_session.cmd_status_output("ethtool -l %s" % iface_name)
63+
if not re.findall("Combined:.*?%s" % new_queues, output):
64+
test.fail("Failed to set combined queues: %s" % new_queues)
65+
66+
def setup_vm_interface():
67+
"""
68+
Setup VM interface according to configuration
69+
"""
70+
vm_xml = VMXML.new_from_inactive_dumpxml(vm_name)
71+
vm_xml.remove_all_device_by_type('interface')
72+
vm_xml.sync()
73+
if interface_timing == "hotplug":
74+
if not vm.is_alive():
75+
vm.start()
76+
iface = libvirt_vmxml.create_vm_device_by_type('interface', iface_dict)
77+
virsh.attach_device(vm_name, iface.xml, flagstr="--config",
78+
debug=True, ignore_status=False)
79+
else:
80+
libvirt_vmxml.modify_vm_device(
81+
VMXML.new_from_inactive_dumpxml(vm_name), 'interface',
82+
iface_dict)
83+
vm.start()
84+
vm.wait_for_serial_login().close()
85+
test.log.debug("Guest xml:\n%s", VMXML.new_from_dumpxml(vm_name))
86+
87+
def cleanup_nvram():
88+
"""
89+
Clean up NVRAM file to avoid UEFI boot issues
90+
"""
91+
nvram_file = f"/var/lib/libvirt/qemu/nvram/{vm_name}_VARS.fd"
92+
if os.path.exists(nvram_file):
93+
test.log.info(f"Removing NVRAM file: {nvram_file}")
94+
os.remove(nvram_file)
95+
96+
def setup_test():
97+
"""
98+
Setup test environment for migration with bridge type interface
99+
"""
100+
test.log.info("Setting up test environment")
101+
# Clean up NVRAM to avoid UEFI boot issues
102+
cleanup_nvram()
103+
104+
if bridge_type == "linux":
105+
utils_net.create_linux_bridge_tmux(bridge_name, iface_name=host_iface)
106+
107+
remote_host_iface = utils_net.get_default_gateway(
108+
iface_name=True, session=remote_session, force_dhcp=True, json=True)
109+
params.update({"remote_host_iface": remote_host_iface})
110+
utils_net.create_linux_bridge_tmux(
111+
bridge_name, iface_name=remote_host_iface, session=remote_session)
112+
113+
elif bridge_type == "ovs":
114+
utils_net.create_ovs_bridge(ovs_bridge_name, ip_options='-color=never')
115+
116+
utils_net.create_ovs_bridge(ovs_bridge_name, session=remote_session,
117+
ip_options='-color=never')
118+
libvirt_network.create_or_del_network(network_dict, remote_args=remote_virsh_dargs)
119+
libvirt_network.create_or_del_network(network_dict)
120+
121+
if vm.is_alive():
122+
vm.destroy()
123+
124+
setup_vm_interface()
125+
126+
def run_test():
127+
"""
128+
Run the main test: migration and verification
129+
"""
130+
test.log.info("TEST_STEP: Migrating VM to target host")
131+
migration_obj.setup_connection()
132+
if not vm.is_alive():
133+
vm.start()
134+
migration_obj.run_migration()
135+
136+
if not cancel_migration:
137+
test.log.info("TEST_STEP: Checking VM network connectivity on target host")
138+
backup_uri, vm.connect_uri = vm.connect_uri, dest_uri
139+
if vm.serial_console is not None:
140+
vm.cleanup_serial_console()
141+
vm.create_serial_console()
142+
vm_session_after_mig = vm.wait_for_serial_login(timeout=240)
143+
vm_session_after_mig.cmd("dhclient -r; dhclient", timeout=120)
144+
145+
test.log.info("TEST_STEP: Testing guest ping to outside")
146+
ips = {'outside_ip': outside_ip}
147+
network_base.ping_check(params, ips, vm_session_after_mig)
148+
149+
test.log.info("TEST_STEP: Checking multiqueue")
150+
check_multiqueue_in_guest(vm_session_after_mig)
151+
virsh_obj = virsh.VirshPersistent(uri=dest_uri)
152+
libvirt_vmxml.check_guest_xml_by_xpaths(
153+
VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_obj),
154+
expected_xpath)
155+
156+
if migrate_vm_back:
157+
test.log.info("TEST_STEP: Migrating VM back to source host")
158+
migration_obj.run_migration_back()
159+
160+
def teardown_test():
161+
"""
162+
Cleanup test environment
163+
"""
164+
test.log.info("Cleaning up test environment")
165+
if bridge_type == "linux":
166+
utils_net.delete_linux_bridge_tmux(bridge_name, iface_name=host_iface)
167+
utils_net.delete_linux_bridge_tmux(
168+
bridge_name, iface_name=params.get("remote_host_iface"),
169+
session=remote_session)
170+
171+
elif bridge_type == "ovs":
172+
utils_net.delete_ovs_bridge(ovs_bridge_name, ip_options='-color=never')
173+
utils_net.delete_ovs_bridge(ovs_bridge_name, session=remote_session, ip_options='-color=never')
174+
175+
libvirt_network.create_or_del_network(network_dict, is_del=True, remote_args=remote_virsh_dargs)
176+
libvirt_network.create_or_del_network(network_dict, is_del=True)
177+
migration_obj.cleanup_connection()
178+
179+
server_ip = params["server_ip"] = params.get("migrate_dest_host")
180+
server_user = params.get("server_user")
181+
server_pwd = params.get("server_pwd") or params.get("migrate_dest_pwd")
182+
outside_ip = params.get("outside_ip")
183+
bridge_name = params.get("bridge_name")
184+
bridge_type = params.get("bridge_type")
185+
ovs_bridge_name = params.get("ovs_bridge_name")
186+
network_dict = eval(params.get("network_dict", "{}"))
187+
interface_timing = params.get("interface_timing")
188+
iface_queues = int(params.get("iface_queues", "5"))
189+
new_queues = int(params.get("new_queues", "3"))
190+
migrate_vm_back = params.get_boolean("migrate_vm_back")
191+
cancel_migration = params.get_boolean("cancel_migration")
192+
193+
remote_virsh_dargs = {'remote_ip': server_ip, 'remote_user': server_user,
194+
'remote_pwd': server_pwd, 'unprivileged_user': params.get("unprivileged_user"),
195+
'ssh_remote_auth': params.get("ssh_remote_auth")}
196+
197+
src_uri = params.get("virsh_migrate_connect_uri")
198+
dest_uri = params.get("virsh_migrate_desturi")
199+
expected_xpath = eval(params.get("expected_xpath"))
200+
iface_dict = eval(params.get("iface_dict", "{}"))
201+
host_iface = params.get('host_iface')
202+
host_iface = host_iface if host_iface else utils_net.get_default_gateway(
203+
iface_name=True, force_dhcp=True, json=True)
204+
205+
vm_name = guest_os.get_vm(params)
206+
vm = env.get_vm(vm_name)
207+
new_xml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name)
208+
209+
remote_session = aexpect.remote.remote_login("ssh", server_ip, "22",
210+
server_user, server_pwd,
211+
r'[$#%]')
212+
migration_obj = base_steps.MigrationBase(test, vm, params)
213+
214+
try:
215+
setup_test()
216+
run_test()
217+
finally:
218+
teardown_test()
219+
if remote_session:
220+
remote_session.close()

0 commit comments

Comments
 (0)