-
Notifications
You must be signed in to change notification settings - Fork 179
virtual_network: add case for pktgen with burst mode #6531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| - virtual_network.qemu_test.pktgen_burst_mode_test: | ||
| type = pktgen_burst_mode_test | ||
| start_vm = no | ||
| category = "tx" | ||
| pktgen_script = "pktgen_sample03_burst_single_flow" | ||
| pktgen_threads = 1 | ||
| pkt_size = 64 | ||
| burst = 3 | ||
| pktgen_test_timeout = 30 | ||
| record_list = "pkt_size run_threads burst mpps" | ||
| guest_ver_cmd = "uname -r" | ||
| kvm_ver_chk_cmd = "rpm -qa qemu-kvm-rhev && rpm -qa qemu-kvm" | ||
| queues = 8 | ||
| test_vm = no | ||
| disable_iptables_rules_cmd = "iptables -F ; nft flush ruleset" | ||
| variants: | ||
| - ethernet_multi_queues: | ||
| iface_type = "ethernet" | ||
| pre_tap = "yes" | ||
| iface_list = [{'type_name': '${iface_type}', 'model': 'virtio', 'target': {'dev': tap_name, 'managed': 'no'},'driver': {'driver_attr': {'queues': '${queues}', 'name': 'vhost'}}}] | ||
| tap_flag = "multi_queue" | ||
| - linux_bridge: | ||
| iface_type = "bridge" | ||
| iface_list = [{'type_name': '${iface_type}', 'model': 'virtio', 'source': {'bridge': bridge_name}, 'driver': {'driver_attr': {'queues': '${queues}', 'name': 'vhost'}}}] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| # | ||
| # Copyright Redhat | ||
| # | ||
| # SPDX-License-Identifier: GPL-2.0 | ||
| # Author: Nannan Li<[email protected]> | ||
| # | ||
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| import os | ||
|
|
||
| from avocado.utils import process | ||
|
|
||
| from virttest import utils_misc | ||
| from virttest import utils_net | ||
| from virttest import virsh | ||
| from virttest.libvirt_xml import vm_xml | ||
|
|
||
| from provider.virtual_network import pktgen_utils | ||
| from provider.virtual_network import network_base | ||
|
|
||
|
|
||
| def write_in_result_file(test, params): | ||
| """ | ||
| Write some qemu, kvm version info and write them into pktgen test result. | ||
| :params, params | ||
| :return result_file, pktgen test result file. | ||
| """ | ||
| kvm_ver_chk_cmd = params.get("kvm_ver_chk_cmd") | ||
|
|
||
| result_path = utils_misc.get_path(test.resultsdir, "pktgen_perf.RHS") | ||
| result_file = open(result_path, "w") | ||
| kvm_ver = process.system_output(kvm_ver_chk_cmd, shell=True).decode() | ||
| host_ver = os.uname()[2] | ||
| result_file.write("### kvm-userspace-ver : %s\n" % kvm_ver) | ||
| result_file.write("### kvm_version : %s\n" % host_ver) | ||
|
|
||
| return result_file | ||
|
|
||
|
|
||
| def run(test, params, env): | ||
| """ | ||
| Pktgen tests with burst mode. | ||
| """ | ||
| def setup_test(): | ||
| """ | ||
| Prepare the test environment with required guest type and interface. | ||
| """ | ||
| utils_net.create_linux_bridge_tmux(bridge_name, host_iface) | ||
| if params.get_boolean("pre_tap"): | ||
| network_base.create_tap(tap_name, bridge_name, 'root', flag=tap_flag) | ||
|
|
||
| test.log.info("TEST_SETUP: guest and interface.") | ||
| network_base.prepare_single_vm(params, vm_name, iface_list) | ||
|
|
||
|
Comment on lines
+56
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prepare_single_vm called with wrong argument order (iface_list passed as disk_path) This breaks VM prep. Pass by keyword to avoid misbinding. - network_base.prepare_single_vm(params, vm_name, iface_list)
+ network_base.prepare_single_vm(params, vm_name, iface_list=iface_list)🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nanli1 ^^ AFAICT coderabbitai is correct, the third argument in your network_base.py of this commit is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| def run_test(): | ||
| """ | ||
| Run pktgen in burst mode for vm. | ||
| """ | ||
| result_file = write_in_result_file(test, params) | ||
|
|
||
| if disable_iptables_rules_cmd: | ||
| process.system(disable_iptables_rules_cmd, shell=True) | ||
|
|
||
| pktgen_runner = pktgen_utils.PktgenRunner() | ||
smitterl marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+64
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Save and restore firewall rules around the test. Avoid leaving the system with flushed rules. if disable_iptables_rules_cmd:
- process.system(disable_iptables_rules_cmd, shell=True)
+ # Save current firewall state
+ ipt_bak = "/tmp/iptables.rules.bak"
+ nft_bak = "/tmp/nft.rules.bak"
+ process.system(f"iptables-save > {ipt_bak}", shell=True)
+ process.system(f"nft list ruleset > {nft_bak}", shell=True)
+ process.system(disable_iptables_rules_cmd, shell=True)
@@
- utils_net.delete_linux_bridge_tmux(bridge_name, host_iface)
+ utils_net.delete_linux_bridge_tmux(bridge_name, host_iface)
+ # Restore firewall if we changed it
+ if disable_iptables_rules_cmd:
+ process.system("iptables-restore < /tmp/iptables.rules.bak", shell=True, ignore_status=True)
+ process.system("nft -f /tmp/nft.rules.bak", shell=True, ignore_status=True)Also applies to: 113-126 🧰 Tools🪛 Ruff (0.13.1)59-59: Function call with (S604)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What will haveppen if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: Length of output: 5089 @smitterl You're absolutely right to be concerned. If The solution is to use nested try-finally blocks or wrap each cleanup operation independently to ensure all cleanup steps attempt to execute even if one fails. Here's a recommended approach: def run(test, params, env):
"""
Pktgen tests with burst mode.
"""
# ... [setup_test and run_test definitions remain the same] ...
def teardown_test():
"""
Clean data.
"""
test.log.debug("TEST_TEARDOWN: Recover the env.")
# Restore firewall first (if it was disabled)
if disable_iptables_rules_cmd and os.path.exists("/tmp/iptables.rules.bak"):
try:
process.system("iptables-restore < /tmp/iptables.rules.bak",
shell=True, ignore_status=True)
process.system("nft -f /tmp/nft.rules.bak",
shell=True, ignore_status=True)
os.remove("/tmp/iptables.rules.bak")
os.remove("/tmp/nft.rules.bak")
except Exception as e:
test.log.warning("Failed to restore firewall rules: %s", e)
# Delete tap (independent cleanup)
try:
network_base.delete_tap(tap_name)
except Exception as e:
test.log.warning("Failed to delete tap %s: %s", tap_name, e)
# Delete bridge (independent cleanup)
try:
utils_net.delete_linux_bridge_tmux(bridge_name, host_iface)
except Exception as e:
test.log.warning("Failed to delete bridge %s: %s", bridge_name, e)
# Restore VM XML (independent cleanup)
try:
backup_vmxml.sync()
except Exception as e:
test.log.warning("Failed to restore VM XML: %s", e)
# ... [variable initialization remains the same] ...
try:
setup_test()
run_test()
finally:
teardown_test()And in def run_test():
"""
Run pktgen in burst mode for vm.
"""
result_file = write_in_result_file(test, params)
if disable_iptables_rules_cmd:
# Save current firewall state
try:
process.system("iptables-save > /tmp/iptables.rules.bak", shell=True)
process.system("nft list ruleset > /tmp/nft.rules.bak", shell=True)
except Exception as e:
test.log.warning("Failed to save firewall rules: %s", e)
process.system(disable_iptables_rules_cmd, shell=True)
# ... [rest of run_test remains the same] ...This approach ensures that:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @coderabbitai - I'm a fan of using a list of cleanup actions defined as lambdas that are added when they become necessary: do_something(x)
cleanup_actions.append(lambda: undo_something(x))
do_something_else_that_does_not_require_cleanup(x)
...
finally:
cleanup_actions.reverse()
for action in cleaup_actions:
try:
action()
except Exception as e:
logging.warning(f"Failed to cleanup: {action} raised {e}")There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @smitterl That's a much cleaner pattern! The lambda list approach is more maintainable and makes it explicit when each cleanup action is registered. Here's how to apply it to this test: def run(test, params, env):
"""
Pktgen tests with burst mode.
"""
cleanup_actions = []
def setup_test():
"""
Prepare the test environment with required guest type and interface.
:return vm_list: list of vm names.
"""
utils_net.create_linux_bridge_tmux(bridge_name, host_iface)
cleanup_actions.append(lambda: utils_net.delete_linux_bridge_tmux(bridge_name, host_iface))
network_base.create_tap(tap_name, bridge_name, 'root', flag=tap_flag)
cleanup_actions.append(lambda: network_base.delete_tap(tap_name))
test.log.info("TEST_SETUP: Prepare specific guest os version and interface.")
vm_list = network_base.prepare_vms_with_iface(
params, vm_image_path=vm_image_paths, iface_list=iface_list)
return vm_list
def run_test(vm_list):
"""
Run pktgen in burst mode for vm.
"""
result_file = write_in_result_file(test, params)
cleanup_actions.append(lambda: result_file.close())
if disable_iptables_rules_cmd:
# Save current firewall state
process.system("iptables-save > /tmp/iptables.rules.bak", shell=True, ignore_status=True)
process.system("nft list ruleset > /tmp/nft.rules.bak", shell=True, ignore_status=True)
cleanup_actions.append(lambda: (
process.system("iptables-restore < /tmp/iptables.rules.bak",
shell=True, ignore_status=True),
process.system("nft -f /tmp/nft.rules.bak",
shell=True, ignore_status=True)
))
process.system(disable_iptables_rules_cmd, shell=True)
# ... rest of run_test ...
# ... variable initialization ...
try:
vm_list = setup_test()
run_test(vm_list)
finally:
test.log.debug("TEST_TEARDOWN: Executing cleanup actions.")
# Execute in reverse order (LIFO - undo in opposite order of setup)
for action in reversed(cleanup_actions):
try:
action()
except Exception as e:
test.log.warning(f"Cleanup action failed: {e}")
# VM XML restore should happen last
try:
backup_vmxml.sync()
except Exception as e:
test.log.warning(f"Failed to restore VM XML: {e}")The key benefits of this approach:
✏️ Learnings added
🧠 Learnings used |
||
| if pktgen_runner.is_version_lt_rhel(process.getoutput("uname -r"), 7): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of process.getoutput it's better to use python's |
||
| if host_ver.count("64k"): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO this is an odd un-pythonic way to check, why not use simply |
||
| pktgen_runner.install_package(host_ver, pagesize="64k") | ||
| else: | ||
| pktgen_runner.install_package(host_ver) | ||
|
|
||
|
Comment on lines
+68
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Version check helper semantics likely inverted; package install gating is wrong
|
||
| test.log.debug("TEST_STEP: Test with guest: %s.", vm_name) | ||
| env.create_vm(vm_type='libvirt', target="", name=vm_name, params=params, bindir=test.bindir) | ||
| vm = env.get_vm(vm_name) | ||
| vm.start() | ||
| test.log.debug("Guest xml:%s", vm_xml.VMXML.new_from_dumpxml(vm_name)) | ||
|
|
||
| test.log.debug("TEST_STEP: Test with guest and host connectivity") | ||
| session_serial = vm.wait_for_serial_login(restart_network=True) | ||
| mac = vm.get_mac_address() | ||
| params.update({ | ||
| 'vm_ping_host': 'pass'} | ||
| ) | ||
| ips = { | ||
| 'vm_ip': utils_net.get_guest_ip_addr(session_serial, mac), | ||
| 'host_ip': host_ip | ||
| } | ||
| network_base.ping_check(params, ips, session_serial, force_ipv4=True) | ||
|
|
||
| test.log.debug("TEST_STEP: Install package and Run pktgen in burst mode.") | ||
| guest_ver = session_serial.cmd_output(guest_ver_cmd) | ||
| result_file.write("### guest-kernel-ver :%s" % guest_ver) | ||
| if pktgen_runner.is_version_lt_rhel(session_serial.cmd("uname -r"), 7): | ||
| if guest_ver.count("64k"): | ||
| pktgen_runner.install_package( | ||
| guest_ver.strip(), | ||
| pagesize="64k", | ||
| vm=vm, | ||
| session_serial=session_serial) | ||
| else: | ||
| pktgen_runner.install_package( | ||
| guest_ver.strip(), vm=vm, session_serial=session_serial) | ||
|
|
||
| pktgen_utils.run_tests_for_category( | ||
| params, result_file, test_vm, vm, session_serial) | ||
|
|
||
| test.log.debug("TEST_STEP: Check the guest dmesg without error messages.") | ||
| vm.verify_dmesg() | ||
| vm.verify_kernel_crash() | ||
|
|
||
| virsh.destroy(vm_name) | ||
| virsh.undefine(vm_name, options='--nvram') | ||
|
|
||
| result_file.close() | ||
|
|
||
| def teardown_test(): | ||
| """ | ||
| Clean data. | ||
| """ | ||
| test.log.debug("TEST_TEARDOWN: Recover the env.") | ||
| network_base.delete_tap(tap_name) | ||
| utils_net.delete_linux_bridge_tmux(bridge_name, host_iface) | ||
|
|
||
| backup_vmxml.sync() | ||
|
|
||
| vm_name = params.get("main_vm") | ||
| vmxml = vm_xml.VMXML.new_from_inactive_dumpxml(vm_name) | ||
| backup_vmxml = vmxml.copy() | ||
|
|
||
| disable_iptables_rules_cmd = params.get("disable_iptables_rules_cmd") | ||
| guest_ver_cmd = params["guest_ver_cmd"] | ||
| test_vm = params.get_boolean("test_vm") | ||
| host_ver = os.uname()[2] | ||
|
|
||
| rand_id = utils_misc.generate_random_string(3) | ||
| bridge_name = 'br_' + rand_id | ||
| tap_name = 'tap_' + rand_id | ||
| host_iface = utils_net.get_default_gateway(iface_name=True, force_dhcp=True, json=True) | ||
| host_ip = utils_net.get_ip_address_by_interface(host_iface, ip_ver='ipv4') | ||
|
|
||
| iface_list = eval(params.get('iface_list', '{}')) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use
+import ast
+
...
- iface_list = eval(params.get('iface_list', '{}'))
+ iface_list = ast.literal_eval(params.get('iface_list', '{}'))🧰 Tools🪛 Ruff (0.14.1)143-143: Use of possibly insecure function; consider using (S307) 🤖 Prompt for AI Agents |
||
| tap_flag = params.get("tap_flag") | ||
|
|
||
| try: | ||
| setup_test() | ||
| run_test() | ||
|
|
||
| finally: | ||
| teardown_test() | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -470,7 +470,7 @@ def prepare_single_vm(params, vm_name, disk_path='', iface_list=[]): | |||||||||||||||||
| :params disk_path: path of the disk image. | ||||||||||||||||||
| :params iface_list: list of interface for vm. | ||||||||||||||||||
| """ | ||||||||||||||||||
| if vm_name not in virsh.dom_list().stdout_text: | ||||||||||||||||||
| if vm_name not in virsh.dom_list("--all").stdout_text: | ||||||||||||||||||
| params.update( | ||||||||||||||||||
| {"vm_name_bootc": vm_name, | ||||||||||||||||||
| "vm_disk_image_path": disk_path, | ||||||||||||||||||
|
|
@@ -503,7 +503,8 @@ def prepare_vms_with_iface(params, vm_image_path=[], iface_list=[]): | |||||||||||||||||
| vm_list = [] | ||||||||||||||||||
| for image_path in vm_image_path: | ||||||||||||||||||
| guest_version = os.path.basename(image_path) | ||||||||||||||||||
| vm_name = "".join([re.findall("Win|RHEL", guest_version, re.IGNORECASE)[0], "_vm"]) | ||||||||||||||||||
| rand_id = utils_misc.generate_random_string(3) | ||||||||||||||||||
| vm_name = "".join([re.findall("Win|RHEL", guest_version, re.IGNORECASE)[0], "_vm"]) + rand_id | ||||||||||||||||||
| LOG.debug("TEST_LOOP: Test '%s' with guest version '%s'.", vm_name, guest_version) | ||||||||||||||||||
|
Comment on lines
+506
to
508
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❓ Verification inconclusiveHarden VM name derivation; avoid IndexError and reduce collision risk.
Apply: - rand_id = utils_misc.generate_random_string(3)
- vm_name = "".join([re.findall("Win|RHEL", guest_version, re.IGNORECASE)[0], "_vm"]) + rand_id
+ rand_id = utils_misc.generate_random_string(5)
+ m = re.search(r"(Win|RHEL)", guest_version, re.IGNORECASE)
+ base = (m.group(0) if m else os.path.splitext(guest_version)[0])
+ vm_name = f"{base}_vm_{rand_id}"Run to ensure no callers assume a fixed VM name without the random suffix: 🏁 Script executed: #!/bin/bash
rg -nP -C2 '(RHEL|Win)_vm\b' --type=pyLength of output: 0 Harden VM name derivation to avoid IndexError and reduce collision risk
- rand_id = utils_misc.generate_random_string(3)
- vm_name = "".join([re.findall("Win|RHEL", guest_version, re.IGNORECASE)[0], "_vm"]) + rand_id
+ rand_id = utils_misc.generate_random_string(5)
+ m = re.search(r"(Win|RHEL)", guest_version, re.IGNORECASE)
+ base = (m.group(0) if m else os.path.splitext(guest_version)[0])
+ vm_name = f"{base}_vm_{rand_id}"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
|
|
||||||||||||||||||
| prepare_single_vm(params, vm_name, image_path, iface_list) | ||||||||||||||||||
|
|
||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.