Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions libvirt/tests/cfg/virtual_network/qemu/transfer_file_over_ipv6.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
- virtual_network.qemu_test.transfer_file_over_ipv6: image_copy
create_vm_libvirt = yes
master_images_clone = img1
kill_vm_libvirt = yes
ovmf:
kill_vm_libvirt_options = --nvram
type = transfer_file_over_ipv6
vms = "vm1 vm2"
image_snapshot = yes
filesize = 4096
file_trans_timeout = 2400
file_md5_check_timeout = 600
dd_cmd = "dd if=/dev/zero of=%s bs=1M count=%d"
Linux:
tmp_dir = "/var/tmp/"
Windows:
tmp_dir = "C:\\"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify Windows path escaping.

The Windows temporary directory path uses a single backslash. Depending on how the configuration parser handles this string, it may need to be escaped as C:\\\\ or use a raw string format.

Run the following script to check how similar Windows paths are defined in other test configurations:


🏁 Script executed:

#!/bin/bash
# Description: Check Windows tmp_dir path format in other configuration files

# Search for Windows tmp_dir definitions in other test configs
rg -n --type=cfg -A1 -B1 'Windows:' libvirt/tests/cfg/ | rg 'tmp_dir'

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Description: Find Windows path definitions in configuration files

# First, find all .cfg files in the test directory
echo "=== Finding .cfg files with Windows references ==="
fd -e cfg -x rg -l 'Windows' {} \;

# Search for tmp_dir patterns in all .cfg files
echo -e "\n=== Searching for tmp_dir patterns in .cfg files ==="
fd -e cfg -x rg -n 'tmp_dir' {} +

# Examine the specific file to understand context
echo -e "\n=== Content of transfer_file_over_ipv6.cfg ==="
cat libvirt/tests/cfg/virtual_network/qemu/transfer_file_over_ipv6.cfg

Length of output: 47424


🏁 Script executed:

#!/bin/bash
# Description: Find the configuration file parser

# Search for files that parse or load .cfg files
echo "=== Finding .cfg file parser implementation ==="
rg -n -i 'class.*config|def.*parse.*cfg|\.cfg' --type=py -A3 -B3 | head -100

# Look for configuration loading functions
echo -e "\n=== Searching for config parsing in Python files ==="
rg -n 'load.*config|parse.*cfg|read.*\.cfg' --type=py -C2 | head -80

# Find import statements related to config/yaml
echo -e "\n=== Searching for YAML or config parsers ==="
rg -n 'import.*yaml|import.*config|from.*yaml|from.*config' --type=py | head -30

Length of output: 18756



🏁 Script executed:

#!/bin/bash
# Description: Find where .cfg files are loaded

# Search for where .cfg configuration files are loaded
echo "=== Finding where .cfg files are loaded ==="
rg -n 'load.*\.cfg|parse.*\.cfg|open.*\.cfg' --type=py -B2 -A2 | head -60

# Look for YAML loading specifically
echo -e "\n=== Finding yaml.load usage ==="
rg -n 'yaml\.load|yaml\.safe_load' --type=py -B3 -A3

# Search for how params are retrieved from config (params.get pattern)
echo -e "\n=== Looking for params dictionary source ==="
rg -n 'params\s*=' --type=py | grep -E 'config|cfg|load' | head -20

Length of output: 2627


🏁 Script executed:

#!/bin/bash
# Description: Find where .cfg files are loaded and how params are parsed

# Look for file loading patterns
echo "=== Searching for test configuration loading ==="
find . -name "*.py" -type f | xargs grep -l "\.cfg" | head -10 | xargs grep -n "open\|load" | grep -i "cfg\|config" | head -40

# Search for the actual config file parser
echo -e "\n=== Looking for configuration module ==="
rg -n 'class.*Config|def.*load_config' --type=py -A5 | head -60

# Check if there's documentation about escape sequences
echo -e "\n=== Check for escape sequence handling in tests ==="
rg -n 'escape|backslash|\\\\' --type=py libvirt/tests/src/virtual_network/ | head -20

Length of output: 143


🌐 Web query:

YAML string escaping rules backslash double quotes

💡 Result:

Short summary — rules for backslash and double quotes in YAML strings:

  • Single-quoted scalars: backslash has no special meaning (it is a literal character). The only escape is to double a single quote: 'He''s'. Use single quotes when you want literal backslashes. (yaml.info)

  • Double-quoted scalars: backslash introduces escape sequences (JSON/C-like). You must escape the double-quote (") and the backslash (\). Supported escapes include common ones (\n, \t, \r, \b, \f, \a, \0), hex/unicode (\xNN, \uNNNN, \UNNNNNNNN), and special YAML escapes; an escaped line break is ignored (allows multiline). Invalid escape sequences are an error. Examples:

    • "She said "Hi""
    • "C:\Program Files\App"
    • "Line1\nLine2" (yaml.org)

References:

  • YAML 1.2 / 1.2.2 spec — escaped characters & double-quoted scalars. (yaml.org)
  • Practical summary (yaml.info) — quoting behavior and examples. (yaml.info)

If you want, I can list the full escape sequence table from the spec or give parser-specific notes (some YAML parsers differ slightly).

Citations:


🏁 Script executed:

#!/bin/bash
# Description: Find how tmp_dir is used in tests

echo "=== Finding where tmp_dir is used in source code ==="
rg -n 'tmp_dir' --type=py -B2 -A2 | head -80

echo -e "\n=== Searching for file transfer test code ==="
rg -n 'transfer_file_over_ipv6\|tmp_dir.*Windows' --type=py -B3 -A3 | head -50

echo -e "\n=== Checking for Windows path handling in tests ==="
rg -n 'C:\\|Windows.*path|tmp_dir' --type=py libvirt/tests/src/virtual_network/ -B2 -A2 | head -40

Length of output: 9908


Fix Windows tmp_dir path escaping from "C:\\" to "C:\\\\"

In the configuration file, the Windows tmp_dir path uses insufficient escaping for YAML double-quoted strings. In YAML double-quoted strings, backslash introduces escape sequences, and the backslash itself must be escaped as \\.

The current value "C:\\" is parsed as C:\ (single backslash). When concatenated with filenames in the test code (line 81-82 of transfer_file_over_ipv6.py), this produces invalid paths like C:\src-xxx.

To produce the correct path separator, use "C:\\\\" which parses to C:\\ (double backslash), resulting in valid concatenated paths like C:\\src-xxx.

🤖 Prompt for AI Agents
In libvirt/tests/cfg/virtual_network/qemu/transfer_file_over_ipv6.cfg around
line 17, the tmp_dir value uses the YAML double-quoted string "C:\\" which is
parsed as C:\ (single backslash) and causes invalid concatenated paths; change
the value to "C:\\\\" so YAML parses it as C:\\ (double backslash) and
concatenations like C:\\src-xxx become valid.

variants:
- remote_addr:
link_local_ipv6_addr = false
- link_local_addr:
link_local_ipv6_addr = true
Host_RHEL.m7, Host_RHEL.m8:
using_guest_interface = true
197 changes: 197 additions & 0 deletions libvirt/tests/src/virtual_network/qemu/transfer_file_over_ipv6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import os
import re

from avocado.utils import crypto, process
from virttest import error_context, remote, utils_misc, utils_net


@error_context.context_aware
def run(test, params, env):
"""
Test Step
1. boot up two virtual machine
2. For linux guest,Transfer data:
host <--> guest1 <--> guest2 <-->host via ipv6
For windows guest,Transfer data:
host <--> guest1&guest2 via ipv6
3. after data transfer, check data have no change
Params:
:param test: LIBVIRT test object
:param params: Dictionary with the test parameters
:param env: Dictionary with test environment.
"""
timeout = params.get_numeric("login_timeout", "360")
client = params.get("file_transfer_client")
port = params.get("file_transfer_port")
password = params.get("password")
username = params.get("username")
tmp_dir = params["tmp_dir"]
filesize = params.get_numeric("filesize", "4096")
dd_cmd = params["dd_cmd"]
file_trans_timeout = params.get_numeric("file_trans_timeout", "1200")
file_md5_check_timeout = params.get_numeric("file_md5_check_timeout", "600")
link_local_ipv6_addr = params.get_boolean("link_local_ipv6_addr")

def get_file_md5sum(file_name, session, timeout):
"""
Get file md5sum from guest.
"""
test.log.info("Get md5sum of the file:'%s'", file_name)
s, o = session.cmd_status_output("md5sum %s" % file_name, timeout=timeout)
if s != 0:
test.error("Get file md5sum failed as %s" % o)
return re.findall(r"\w{32}", o)[0]
Comment on lines +35 to +43
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Verify Windows compatibility for MD5 calculation.

The get_file_md5sum function executes md5sum, which is a Linux command. Windows guests may not have this command available by default. The test transfers files to Windows guests (line 93-116) and would fail when calculating MD5 on Windows.

Consider adding OS-specific MD5 command logic:

 def get_file_md5sum(file_name, session, timeout):
     """
     Get file md5sum from guest.
     """
     test.log.info("Get md5sum of the file:'%s'", file_name)
-    s, o = session.cmd_status_output("md5sum %s" % file_name, timeout=timeout)
+    if params.get("os_type") == "linux":
+        cmd = "md5sum %s" % file_name
+    else:
+        cmd = "certutil -hashfile %s MD5" % file_name
+    s, o = session.cmd_status_output(cmd, timeout=timeout)
     if s != 0:
         test.error("Get file md5sum failed as %s" % o)
     return re.findall(r"\w{32}", o)[0]

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In libvirt/tests/src/virtual_network/qemu/transfer_file_over_ipv6.py around
lines 35 to 43, the helper always runs the Linux-only "md5sum" which will fail
on Windows guests; update get_file_md5sum to detect the guest OS (or accept an
is_windows/session.os flag), run a Windows-safe command such as "certutil
-hashfile <file> MD5" or PowerShell "Get-FileHash -Algorithm MD5 <file>" when
the guest is Windows, parse the command output to extract the hex MD5, keep the
existing md5sum branch for Unix, and handle non-zero exit codes with test.error
including the command output.


sessions = {}
addresses = {}
inet_name = {}
vms = []

error_context.context("Boot vms for test", test.log.info)
for vm_name in params.get("vms", "vm1 vm2").split():
vms.append(env.get_vm(vm_name))

host_ifname = params.get("netdst") if link_local_ipv6_addr else None
host_address = utils_net.get_host_ip_address(
params, ip_ver="ipv6", linklocal=link_local_ipv6_addr
)

error_context.context("Get ipv6 address of host: %s" % host_address, test.log.info)
for vm in vms:
vm.verify_alive()
sessions[vm] = vm.wait_for_login(timeout=timeout)
if params.get("os_type") == "linux":
inet_name[vm] = utils_net.get_linux_ifname(
sessions[vm], vm.get_mac_address()
)
addresses[vm] = utils_net.get_guest_ip_addr(
sessions[vm],
vm.get_mac_address(),
params.get("os_type"),
ip_version="ipv6",
linklocal=link_local_ipv6_addr,
)
if link_local_ipv6_addr is False and addresses[vm] is None:
test.cancel("Your guest can not get remote IPv6 address.")
error_context.context(
"Get ipv6 address of %s: %s" % (vm.name, addresses[vm]), test.log.info
)
Comment on lines +74 to +78
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove unreachable code after test.cancel.

Lines 76-78 are unreachable because test.cancel() on line 75 raises an exception and exits the test. The error_context.context() call will never execute.

Apply this diff to remove unreachable code and move the context logging before the cancel:

-        if link_local_ipv6_addr is False and addresses[vm] is None:
-            test.cancel("Your guest can not get remote IPv6 address.")
-            error_context.context(
-                "Get ipv6 address of %s: %s" % (vm.name, addresses[vm]), test.log.info
-            )
+        error_context.context(
+            "Get ipv6 address of %s: %s" % (vm.name, addresses[vm]), test.log.info
+        )
+        if link_local_ipv6_addr is False and addresses[vm] is None:
+            test.cancel("Your guest can not get remote IPv6 address.")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if link_local_ipv6_addr is False and addresses[vm] is None:
test.cancel("Your guest can not get remote IPv6 address.")
error_context.context(
"Get ipv6 address of %s: %s" % (vm.name, addresses[vm]), test.log.info
)
error_context.context(
"Get ipv6 address of %s: %s" % (vm.name, addresses[vm]), test.log.info
)
if link_local_ipv6_addr is False and addresses[vm] is None:
test.cancel("Your guest can not get remote IPv6 address.")
🤖 Prompt for AI Agents
In libvirt/tests/src/virtual_network/qemu/transfer_file_over_ipv6.py around
lines 74 to 78, the error_context.context() call after test.cancel() is
unreachable because test.cancel() raises and exits; move the
error_context.context(...) call so it runs before calling test.cancel(), then
remove the now-unreachable lines after test.cancel() so logging occurs and no
dead code remains.


# prepare test data
guest_path = tmp_dir + "src-%s" % utils_misc.generate_random_string(8)
dest_path = tmp_dir + "dst-%s" % utils_misc.generate_random_string(8)
host_path = os.path.join(
test.tmpdir, "tmp-%s" % utils_misc.generate_random_string(8)
)
test.log.info("Test setup: Creating %dMB file on host", filesize)
process.run(dd_cmd % (host_path, filesize), shell=True)

try:
src_md5 = crypto.hash_file(host_path, algorithm="md5")
error_context.context("md5 value of data from src: %s" % src_md5, test.log.info)
# transfer data
for vm in vms:
error_context.context(
"Transfer data from host to %s" % vm.name, test.log.info
)
remote.copy_files_to(
addresses[vm],
client,
username,
password,
port,
host_path,
guest_path,
timeout=file_trans_timeout,
interface=host_ifname,
)
dst_md5 = get_file_md5sum(
guest_path, sessions[vm], timeout=file_md5_check_timeout
)
error_context.context(
"md5 value of data in %s: %s" % (vm.name, dst_md5), test.log.info
)
if dst_md5 != src_md5:
test.fail("File changed after transfer host -> %s" % vm.name)

if params.get("os_type") == "linux":
for vm_src in addresses:
for vm_dst in addresses:
if vm_src != vm_dst:
error_context.context(
"Transferring data from %s to %s"
% (vm_src.name, vm_dst.name),
test.log.info,
)
if params.get_boolean("using_guest_interface"):
dst_interface = inet_name[vm_src]
else:
dst_interface = host_ifname
Comment on lines +126 to +129
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix interface selection logic for guest-to-guest transfer.

When using_guest_interface is true, the destination interface should be from the destination VM (vm_dst), not the source VM (vm_src). The current logic appears reversed.

Apply this diff:

                         if params.get_boolean("using_guest_interface"):
-                            dst_interface = inet_name[vm_src]
+                            dst_interface = inet_name[vm_dst]
                         else:
                             dst_interface = host_ifname
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if params.get_boolean("using_guest_interface"):
dst_interface = inet_name[vm_src]
else:
dst_interface = host_ifname
if params.get_boolean("using_guest_interface"):
dst_interface = inet_name[vm_dst]
else:
dst_interface = host_ifname
🤖 Prompt for AI Agents
In libvirt/tests/src/virtual_network/qemu/transfer_file_over_ipv6.py around
lines 126 to 129, the branch that sets dst_interface when
params.get_boolean("using_guest_interface") is selecting inet_name[vm_src]
(source VM) incorrectly; change it to use inet_name[vm_dst] so the destination
interface comes from the destination VM. Update that assignment only, ensuring
vm_dst is in scope and used for the guest-to-guest transfer path.

remote.scp_between_remotes(
addresses[vm_src],
addresses[vm_dst],
port,
password,
password,
username,
username,
guest_path,
dest_path,
timeout=file_trans_timeout,
src_inter=host_ifname,
dst_inter=dst_interface,
)
dst_md5 = get_file_md5sum(
dest_path, sessions[vm_dst], timeout=file_md5_check_timeout
)
error_context.context(
"md5 value of data in %s: %s" % (vm.name, dst_md5),
test.log.info,
)
Comment on lines +147 to +150
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix incorrect variable reference in error context.

Line 148 uses vm.name but should use vm_dst.name since we're logging the MD5 value for the destination VM in the guest-to-guest transfer loop.

Apply this diff:

                         dst_md5 = get_file_md5sum(
                             dest_path, sessions[vm_dst], timeout=file_md5_check_timeout
                         )
                         error_context.context(
-                            "md5 value of data in %s: %s" % (vm.name, dst_md5),
+                            "md5 value of data in %s: %s" % (vm_dst.name, dst_md5),
                             test.log.info,
                         )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
error_context.context(
"md5 value of data in %s: %s" % (vm.name, dst_md5),
test.log.info,
)
error_context.context(
"md5 value of data in %s: %s" % (vm_dst.name, dst_md5),
test.log.info,
)
🤖 Prompt for AI Agents
In libvirt/tests/src/virtual_network/qemu/transfer_file_over_ipv6.py around
lines 147 to 150, the error_context.context call logs the MD5 for the
destination VM but incorrectly references vm.name; change that reference to
vm_dst.name so the message reads the destination VM's name (i.e., replace
vm.name with vm_dst.name in the formatted string).

if dst_md5 != src_md5:
test.fail(
"File changed transfer %s -> %s"
% (vm_src.name, vm_dst.name)
)

for vm in vms:
error_context.context(
"Transfer data from %s to host" % vm.name, test.log.info
)
remote.copy_files_from(
addresses[vm],
client,
username,
password,
port,
guest_path,
host_path,
timeout=file_trans_timeout,
interface=host_ifname,
)
error_context.context(
"Check whether the file changed after trans", test.log.info
)
dst_md5 = crypto.hash_file(host_path, algorithm="md5")
error_context.context(
"md5 value of data after copying to host: %s" % dst_md5, test.log.info
)

if dst_md5 != src_md5:
test.fail("File changed after transfer (md5sum mismatch)")
process.system_output("rm -rf %s" % host_path, timeout=timeout)

finally:
process.system("rm -rf %s" % host_path, timeout=timeout, ignore_status=True)
for vm in vms:
if params.get("os_type") == "linux":
sessions[vm].cmd(
"rm -rf %s %s || true" % (guest_path, dest_path),
timeout=timeout,
ignore_all_errors=True,
)
else:
sessions[vm].cmd(
"del /f %s" % guest_path, timeout=timeout, ignore_all_errors=True
)
sessions[vm].close()