Skip to content

Commit 7fef28a

Browse files
committed
Add interface attachment test with target option for nat and direct types
This commit introduces test cases for attaching network interfaces with the --target option specifically for NAT and direct network types. Reference: xxxx-296606 Signed-off-by: nanli <[email protected]>
1 parent a095fab commit 7fef28a

File tree

2 files changed

+151
-1
lines changed

2 files changed

+151
-1
lines changed

libvirt/tests/cfg/virsh_cmd/domain/virsh_attach_detach_interface.cfg

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,33 @@
5454
attach_cmd = 'attach-device'
5555
save_restore = 'yes'
5656
at_detach_iface_coalesce = {'max': '32'}
57+
- target_option_test:
58+
# Test attach-interface with --target option for network interface type
59+
# Test matrix based on target name behavior for network type
60+
only vm_running..domname
61+
check_xml_result = "no"
62+
check_domiflist_result = "yes"
63+
check_guest_interface = "yes"
64+
test_target_option = "yes"
65+
variants:
66+
- target_vnet90:
67+
at_detach_iface_target = "vnet90"
68+
expected_target_name = "vnet1"
69+
- target_macvtap90:
70+
at_detach_iface_target = "macvtap90"
71+
expected_target_name = "vnet1"
72+
- target_vif90:
73+
at_detach_iface_target = "vif90"
74+
expected_target_name = "vif90"
75+
- target_macvlan90:
76+
at_detach_iface_target = "macvlan90"
77+
expected_target_name = "vnet1"
78+
- target_testname:
79+
at_detach_iface_target = "testname"
80+
expected_target_name = "testname"
81+
- target_unset:
82+
expected_target_name = "vnet1"
83+
none_target = "yes"
5784

5885
- exist_bridge:
5986
at_detach_iface_type = "bridge"
@@ -102,6 +129,35 @@
102129
at_detach_link_state = "up"
103130
at_detach_rom_bar = "{'bar':'on'}"
104131
at_detach_iface_backend = "{'vhost':'/dev/vhost-net'}"
132+
- target_option_test:
133+
# Test attach-interface with --target option for direct interface type
134+
# Test matrix based on target name behavior for direct type
135+
only vm_running..domname
136+
check_xml_result = "no"
137+
check_domiflist_result = "yes"
138+
check_guest_interface = "yes"
139+
test_target_option = "yes"
140+
# Override the default target from parent direct section
141+
at_detach_iface_target = ""
142+
variants:
143+
- target_vnet90:
144+
at_detach_iface_target = "vnet90"
145+
expected_target_name = "macvtap0"
146+
- target_macvtap90:
147+
at_detach_iface_target = "macvtap90"
148+
expected_target_name = "macvtap0"
149+
- target_vif90:
150+
at_detach_iface_target = "vif90"
151+
expected_target_name = "vif90"
152+
- target_macvlan90:
153+
at_detach_iface_target = "macvlan90"
154+
expected_target_name = "macvtap0"
155+
- target_testname:
156+
at_detach_iface_target = "testname"
157+
expected_target_name = "testname"
158+
- target_unset:
159+
expected_target_name = "macvtap0"
160+
none_target = "yes"
105161
# TODO: There are a lot of type:user,ethernet,direct and etc.
106162
variants:
107163
- vm_running:

libvirt/tests/src/virsh_cmd/domain/virsh_attach_detach_interface.py

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from virttest.utils_test import libvirt
1414
from virttest.libvirt_xml import vm_xml
1515
from virttest.libvirt_xml.devices.interface import Interface
16+
from virttest.utils_libvirt import libvirt_misc
1617

1718

1819
# Using as lower capital is not the best way to do, but this is just a
@@ -224,6 +225,79 @@ def check_coalesce(vm_name, mac, coalesce):
224225
return 0, ''
225226

226227

228+
def check_target_option_behavior(test, vm_name, iface_mac, expected_target_name):
229+
"""
230+
Check target option behavior according to test matrix
231+
232+
:param test: Test object for test.fail() calls
233+
:param vm_name: Name of the virtual machine
234+
:param iface_mac: MAC address of the interface to check
235+
:param expected_target_name: Expected exact target name for "keep" behavior
236+
"""
237+
238+
# Get actual target from XML using fetch_attrs
239+
vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name)
240+
interfaces = vmxml.devices.by_device_tag('interface')
241+
actual_target = None
242+
for iface in interfaces:
243+
if iface.mac_address == iface_mac:
244+
actual_target = iface.fetch_attrs().get('target', {}).get('dev')
245+
break
246+
if not re.findall(expected_target_name, actual_target):
247+
test.fail("Expected target '%s', got '%s'" % (expected_target_name, actual_target))
248+
test.log.debug("PASS: Expected target '%s' in vm xml for MAC %s", expected_target_name, iface_mac)
249+
250+
251+
def check_domiflist_target(test, vm_name, iface_mac, expected_target_name, existed=True):
252+
"""
253+
Check target device name from virsh domiflist.
254+
255+
:param test: Test object for test.fail() calls
256+
:param vm_name: Name of the virtual machine
257+
:param iface_mac: MAC address of the interface to find
258+
:param expected_target_name: Expected target device name pattern
259+
:param existed: Whether the target should exist (True) or not exist (False)
260+
:raises: test.fail() if domiflist command fails or MAC not found
261+
"""
262+
result = virsh.domiflist(vm_name, ignore_status=True, debug=True)
263+
for line in result.stdout.strip().split('\n'):
264+
if iface_mac.lower() in line.lower():
265+
iflist_info = libvirt_misc.convert_to_dict(line, pattern=r'(\S+) +(.*)')
266+
target_names = list(iflist_info.keys())[0]
267+
if existed:
268+
if not re.findall(expected_target_name, target_names):
269+
test.fail("Expected '%s' is existed, but got '%s' in domiflist" % (expected_target_name, target_names))
270+
else:
271+
if re.findall(expected_target_name, target_names):
272+
test.fail("Expected '%s' is not existed, but got '%s' in domiflist" % (expected_target_name, target_names))
273+
test.log.debug("Checking PASS: Interface target '%s' with domiflist for MAC %s", expected_target_name, iface_mac)
274+
275+
276+
def check_guest_interface_exists(test, vm, iface_mac, expected_existed=True):
277+
"""
278+
Check if interface exists in guest OS.
279+
280+
:param test: Test object for test.fail() calls
281+
:param vm: Virtual machine object.
282+
:param iface_mac: MAC address of the interface to verify in guest.
283+
:param expected_existed: Expected iface_mac is existed or not.
284+
"""
285+
try:
286+
session = vm.wait_for_login()
287+
status, output = session.cmd_status_output("ip -4 -o link list")
288+
session.close()
289+
if expected_existed:
290+
if not re.search(iface_mac.lower(), output.lower()):
291+
test.fail("Interface with MAC %s was not found in guest: %s" % (iface_mac, output))
292+
else:
293+
if re.search(iface_mac.lower(), output.lower()):
294+
test.fail("Interface with MAC %s was found in guest: %s" % (iface_mac, output))
295+
logging.info("PASS checking for the Interface with MAC %s in guest", iface_mac)
296+
297+
except Exception as e:
298+
test.fail("Failed to check guest interface: %s" % str(e))
299+
300+
227301
def run(test, params, env):
228302
"""
229303
Test virsh {at|de}tach-interface command.
@@ -281,6 +355,14 @@ def run(test, params, env):
281355
paused_after_vm_start = "yes" == params.get("paused_after_vm_start", "no")
282356
machine_type = params.get("machine_type")
283357

358+
# Target option test specific parameters
359+
test_target_option = params.get_boolean("test_target_option")
360+
check_domiflist_result = params.get_boolean("check_domiflist_result")
361+
check_guest_interface = params.get_boolean("check_guest_interface")
362+
expected_target_name = params.get("expected_target_name", "")
363+
if params.get_boolean("none_target"):
364+
iface_target = None
365+
284366
# Get iface name if iface_type is direct
285367
if iface_type == "direct":
286368
links = utils_net.get_net_if(state="UP")
@@ -472,9 +554,17 @@ def _check_coalesce(fail_flag):
472554
# Check coalesce info if needed
473555
fail_flag = _check_coalesce(fail_flag)
474556

557+
# Check attach-interface with --target option part.
558+
if test_target_option and not fail_flag:
559+
check_target_option_behavior(
560+
test, vm_name, iface_mac, expected_target_name)
561+
if check_domiflist_result:
562+
check_domiflist_target(test, vm_name, iface_mac, expected_target_name)
563+
iface_target = params.get("expected_target_name")
564+
475565
# Check on host for direct type
476566
if iface_type == 'direct':
477-
cmd_result = process.run("ip -d link show test").stdout_text.strip()
567+
cmd_result = process.run("ip -d link show %s" % iface_target).stdout_text.strip()
478568
logging.info("cmd output is %s", cmd_result)
479569
check_patten = ("%s@%s.*\n.*%s.*\n.*macvtap.*mode.*%s"
480570
% (iface_target, iface_source, iface_mac, iface_mode))
@@ -521,6 +611,10 @@ def _check_coalesce(fail_flag):
521611
# If command with --config parameter, ignore below checking.
522612
if options_suffix.count("config"):
523613
return
614+
if test_target_option:
615+
check_guest_interface_exists(test, vm, iface_mac, expected_existed=False)
616+
check_domiflist_target(test, vm_name, iface_mac, expected_target_name, existed=False)
617+
fail_flag = 0
524618
# Check the xml after detach and clean up if needed.
525619
time.sleep(5)
526620
status, _ = check_dumpxml_iface(vm_name, iface_format)

0 commit comments

Comments
 (0)