Skip to content

Conversation

@hholoubk
Copy link
Contributor

@hholoubk hholoubk commented Oct 2, 2025

  • Enhanced VIOMMUTest.parse_iface_dict() to handle both single dict and list of dicts
  • Added _update_iface_dict_with_controller_info() helper method for maintainability
  • Updated iommu_device_settings.py to handle multiple interfaces in modify_vm_device()
  • Added two new test configurations for multiple VFIO interfaces:
    • Case 10: Multiple VFIO interfaces under pcie-root-port
    • Case 11: Multiple VFIO interfaces with pcie-expander-bus
  • Maintained backward compatibility with existing single interface configurations
  • All changes pass inspekt code quality checks

Summary by CodeRabbit

  • New Features

    • Added two VFIO scenarios for multi-device testing: PCIe root-port and expander-bus variants.
    • Support for multiple interface entries with automatic controller/address assignment and PCIe-bridge slot handling.
  • Bug Fixes

    • Fixed config key typo (virtio_multi_devices).
    • Improved VM session and serial console setup/cleanup for reliable multi-interface management.

…ests

- Enhanced VIOMMUTest.parse_iface_dict() to handle both single dict and list of dicts
- Added _update_iface_dict_with_controller_info() helper method for maintainability
- Updated iommu_device_settings.py to handle multiple interfaces in modify_vm_device()
- Added two new test configurations for multiple VFIO interfaces:
  * Case 10: Multiple VFIO interfaces under pcie-root-port
  * Case 11: Multiple VFIO interfaces with pcie-expander-bus
- Maintained backward compatibility with existing single interface configurations
- All changes pass inspekt code quality checks
@coderabbitai
Copy link

coderabbitai bot commented Oct 2, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Renames a config variant key and adds two multi‑VFIO topology variants; updates test logic to close VM session earlier, handle single-or-multiple iface dicts and manage serial consoles; enhances provider parsing to attach controller-derived PCI addressing and flexible pre-controller resolution.

Changes

Cohort / File(s) Summary
Config variants and key rename
libvirt/tests/cfg/sriov/vIOMMU/iommu_device_settings.cfg
Renames top-level variant key from virtio_muti_devices to virtio_multi_devices; adds multiple_vfio_pcie_root_port and multiple_vfio_pcie_expander_bus variants including controller/topology, disk, and iface definitions with descriptive comments.
Test session, iface handling & serial console
libvirt/tests/src/sriov/vIOMMU/iommu_device_settings.py
Adds from virttest.utils_test import libvirt; closes vm_session after capturing pre_devices; extends iface cleanup to accept a single dict or a list (removes existing iface(s), creates each new iface, logs XML); calls vm.cleanup_serial_console() and vm.create_serial_console() after VM start; ensures vm_session closed and serial cleanup in finally.
Provider iface parsing & controller addressing
provider/viommu/viommu_base.py
parse_iface_dict accepts/returns either a dict or a list; adds _update_iface_dict_with_controller_info to populate address from controller info (bus/slot) and request PCI slot for pcie-to-pci-bridge; introduces helpers to resolve pre_controller (_find_pre_controller_index, find_by*), _controller_exists_by_index, richer logging, and improved controller preparation and teardown traces.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Test as Test (iface cleanup)
  participant Provider as VIOMMUTest.parse_iface_dict
  participant PCI as libvirt_pcicontr
  participant Controller as Controller resolution helpers

  rect rgba(230,240,255,0.6)
    note over Test,Provider: Input can be a single iface dict or a list
    Test->>Provider: parse_iface_dict(iface_dict(s), controller_dicts)
    Provider->>Controller: _find_pre_controller_index(pre_controller, contr_dict)
    alt pre_controller resolved by dict/index/name
      Controller-->>Provider: index (bus/slot base)
    else fallback to max/index
      Controller-->>Provider: computed index
    end
    alt iface list
      loop per iface
        Provider->>PCI: get_free_pci_slot (if model == "pcie-to-pci-bridge")
        PCI-->>Provider: slot (optional)
        Provider-->>Test: updated iface (with address)
      end
    else single iface
      Provider->>PCI: get_free_pci_slot (conditional)
      PCI-->>Provider: slot (optional)
      Provider-->>Test: updated iface (with address)
    end
  end

  rect rgba(240,255,240,0.6)
    note over Test: Remove existing iface(s) then add each updated iface
    loop per updated iface (or single)
      Test->>Test: remove existing iface (if present)
      Test->>Test: create/add updated iface (modify_vm_device)
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • xiaodwan

Poem

I hop through ports and buses bright,
I count each VF and set its right,
Single or many, neat and done,
Sessions closed before new fun,
A rabbit's stamp — tests run! 🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly summarizes the primary change by stating the addition of multiple VFIO interfaces in the vIOMMU test and includes the tracker ID for reference. It is concise, specific, and aligns with the PR objectives without listing file details or using vague terms.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
provider/viommu/viommu_base.py (1)

106-106: Replace eval() with ast.literal_eval() for security.

Similar to line 120, using eval() here poses a code injection risk. For consistency and security, replace with ast.literal_eval().

Apply this diff:

+from ast import literal_eval
+
 def __init__(self, vm, test, params, session=None):
     self.vm = vm
     self.test = test
     self.params = params
     self.session = session
     self.remote_virsh_dargs = None
-    self.controller_dicts = eval(self.params.get("controller_dicts", "[]"))
+    self.controller_dicts = literal_eval(self.params.get("controller_dicts", "[]"))
🧹 Nitpick comments (1)
libvirt/tests/src/sriov/vIOMMU/iommu_device_settings.py (1)

61-70: Optional: Reduce code duplication.

The modify_vm_device call is duplicated in both branches. Consider refactoring to handle both cases uniformly:

         if cleanup_ifaces:
             # Handle both single dict and list of dicts
-            if isinstance(iface_dict, list):
-                for single_iface_dict in iface_dict:
-                    libvirt_vmxml.modify_vm_device(
-                            vm_xml.VMXML.new_from_dumpxml(vm.name),
-                            "interface", single_iface_dict)
-            else:
-                libvirt_vmxml.modify_vm_device(
-                        vm_xml.VMXML.new_from_dumpxml(vm.name),
-                        "interface", iface_dict)
+            iface_dicts_to_cleanup = iface_dict if isinstance(iface_dict, list) else [iface_dict]
+            for single_iface_dict in iface_dicts_to_cleanup:
+                libvirt_vmxml.modify_vm_device(
+                        vm_xml.VMXML.new_from_dumpxml(vm.name),
+                        "interface", single_iface_dict)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8266542 and bdce0d5.

📒 Files selected for processing (3)
  • libvirt/tests/cfg/sriov/vIOMMU/iommu_device_settings.cfg (2 hunks)
  • libvirt/tests/src/sriov/vIOMMU/iommu_device_settings.py (1 hunks)
  • provider/viommu/viommu_base.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.13.2)
provider/viommu/viommu_base.py

119-119: Local variable mac_addr is assigned to but never used

Remove assignment to unused variable mac_addr

(F841)


120-120: Use of possibly insecure function; consider using ast.literal_eval

(S307)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Python 3.12
  • GitHub Check: Python 3.8
  • GitHub Check: Python 3.11
  • GitHub Check: Python 3.9
🔇 Additional comments (4)
provider/viommu/viommu_base.py (2)

132-148: LGTM!

The helper method cleanly encapsulates controller addressing logic with appropriate special handling for pcie-to-pci-bridge models. The in-place mutation of iface_dict is consistent with the class's pattern and keeps the interface simple.


122-130: Audit parse_iface_dict callers for list return
Outside provider/sRIoV/iommu_device_settings.py, all callers treat the return value as a single dict (e.g., calling .get() or passing it directly to create_iface), which will break if a list is returned. Confirm inputs never produce a list or update callers to handle lists appropriately.

libvirt/tests/cfg/sriov/vIOMMU/iommu_device_settings.cfg (2)

29-29: LGTM! Typo fix improves clarity.

The variant name has been corrected from virtio_muti_devices to virtio_multi_devices, fixing the spelling error.


86-97: vf_pci_addr2 is properly defined in the test suite and available for use; no further action required.

Comment on lines 98 to 115
- multiple_vfio_pcie_expander_bus:
only virtio
need_sriov = yes
ping_dest = ''
test_devices = ["Eth"]
# Multiple VFIO interfaces with pcie-expander-bus
# pcie-root <--- pcie-expander-bus <-- pcie-root-port <--- virtio interface with iommu=on
# I |<----------------- pcie-root-port <--- vfio interface
# | |<------------------pcie-root-port <--- vfio interface
# | <----------pcie-root-port < ---virtio disk with iommu=on
expander_bus = {'type': 'pci', 'model': 'pcie-expander-bus', 'target': {'busNr': '251'}, 'pre_controller': 'pcie-root'}
root_port1 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
root_port2 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
root_port3 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
controller_dicts = [${expander_bus}, ${root_port1}, ${root_port2}, ${root_port3}]
disk_dict = {'target': {'dev': 'vda', 'bus': 'virtio'}, 'device': 'disk', 'driver': ${disk_driver}}
# Mixed interfaces: one virtio network with iommu=on and two VFIO interfaces
iface_dict = [{'type_name': 'network', 'model': 'virtio', 'driver': {'driver_attr': {'name': 'vhost', 'iommu': 'on'}}, 'source': {'network': 'default'}}, {'managed': 'yes', 'type_name': 'hostdev', 'hostdev_address': {'type_name': 'pci', 'attrs': vf_pci_addr}}, {'managed': 'yes', 'type_name': 'hostdev', 'hostdev_address': {'type_name': 'pci', 'attrs': vf_pci_addr2}}]
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 controller count and device placement.

The ASCII diagram in lines 103-107 suggests 4 pcie-root-ports are needed (one each for: virtio network interface, two VFIO interfaces, and virtio disk), but controller_dicts at line 112 only defines 3 root-ports (root_port1, root_port2, root_port3).

Additionally, this variant introduces:

  1. Mixed interface types in iface_dict (line 115): one virtio network and two VFIO hostdevs
  2. Reference to vf_pci_addr2 for the second VFIO interface

Please clarify:

  • Are 3 root-ports sufficient, or should a 4th be added for the disk?
  • Confirm that vf_pci_addr2 is properly defined in the test environment
  • Verify that the test code (parse_iface_dict() and related functions) correctly handles a list with mixed interface types (virtio network + hostdev)

Consider updating the comment or adding a 4th root-port to match the described topology:

         root_port1 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
         root_port2 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
         root_port3 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
-        controller_dicts = [${expander_bus}, ${root_port1}, ${root_port2}, ${root_port3}]
+        root_port4 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
+        controller_dicts = [${expander_bus}, ${root_port1}, ${root_port2}, ${root_port3}, ${root_port4}]
📝 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
- multiple_vfio_pcie_expander_bus:
only virtio
need_sriov = yes
ping_dest = ''
test_devices = ["Eth"]
# Multiple VFIO interfaces with pcie-expander-bus
# pcie-root <--- pcie-expander-bus <-- pcie-root-port <--- virtio interface with iommu=on
# I |<----------------- pcie-root-port <--- vfio interface
# | |<------------------pcie-root-port <--- vfio interface
# | <----------pcie-root-port < ---virtio disk with iommu=on
expander_bus = {'type': 'pci', 'model': 'pcie-expander-bus', 'target': {'busNr': '251'}, 'pre_controller': 'pcie-root'}
root_port1 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
root_port2 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
root_port3 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
controller_dicts = [${expander_bus}, ${root_port1}, ${root_port2}, ${root_port3}]
disk_dict = {'target': {'dev': 'vda', 'bus': 'virtio'}, 'device': 'disk', 'driver': ${disk_driver}}
# Mixed interfaces: one virtio network with iommu=on and two VFIO interfaces
iface_dict = [{'type_name': 'network', 'model': 'virtio', 'driver': {'driver_attr': {'name': 'vhost', 'iommu': 'on'}}, 'source': {'network': 'default'}}, {'managed': 'yes', 'type_name': 'hostdev', 'hostdev_address': {'type_name': 'pci', 'attrs': vf_pci_addr}}, {'managed': 'yes', 'type_name': 'hostdev', 'hostdev_address': {'type_name': 'pci', 'attrs': vf_pci_addr2}}]
root_port1 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
root_port2 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
root_port3 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
root_port4 = {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': 'pcie-expander-bus'}
controller_dicts = [${expander_bus}, ${root_port1}, ${root_port2}, ${root_port3}, ${root_port4}]
🤖 Prompt for AI Agents
libvirt/tests/cfg/sriov/vIOMMU/iommu_device_settings.cfg lines 98-115: the ASCII
topology implies four pcie-root-ports (virtio net, vfio1, vfio2, virtio disk)
but controller_dicts only includes three root_port entries; add a fourth
root_port definition (root_port4) and include it in controller_dicts so the disk
has its own root-port, ensure vf_pci_addr2 is declared in the test fixture or
config before use, and verify/adjust parse_iface_dict() and any related parsing
code to correctly accept a list with mixed interface dicts (handle 'network'
virtio entries and 'hostdev' pci entries in the same list), adding/adjusting
checks or unit tests to validate correct placement of each device on the
expected controllers.

Comment on lines 61 to 70
# Handle both single dict and list of dicts
if isinstance(iface_dict, list):
for single_iface_dict in iface_dict:
libvirt_vmxml.modify_vm_device(
vm_xml.VMXML.new_from_dumpxml(vm.name),
"interface", single_iface_dict)
else:
libvirt_vmxml.modify_vm_device(
vm_xml.VMXML.new_from_dumpxml(vm.name),
"interface", iface_dict)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Consider error handling for multi-interface cleanup.

The current implementation iterates through interface dicts without error handling. If modify_vm_device fails on one interface, subsequent interfaces won't be cleaned up, potentially leaving the VM in an inconsistent state.

Consider wrapping the cleanup calls in a try-except block to ensure all interfaces are attempted:

         if cleanup_ifaces:
             # Handle both single dict and list of dicts
             if isinstance(iface_dict, list):
                 for single_iface_dict in iface_dict:
-                    libvirt_vmxml.modify_vm_device(
-                            vm_xml.VMXML.new_from_dumpxml(vm.name),
-                            "interface", single_iface_dict)
+                    try:
+                        libvirt_vmxml.modify_vm_device(
+                                vm_xml.VMXML.new_from_dumpxml(vm.name),
+                                "interface", single_iface_dict)
+                    except Exception as e:
+                        test.log.warning(f"Failed to cleanup interface: {e}")
             else:
                 libvirt_vmxml.modify_vm_device(
                         vm_xml.VMXML.new_from_dumpxml(vm.name),
                         "interface", iface_dict)

Additionally, verify that parse_iface_dict() validates list contents to prevent non-dict items from reaching this code.


🏁 Script executed:

#!/bin/bash
# Description: Verify parse_iface_dict() implementation and its validation logic

# Search for the parse_iface_dict method definition
ast-grep --pattern $'def parse_iface_dict($$$) {
  $$$
}'

# Also search for type validation or list handling in parse_iface_dict
rg -n -C5 'def parse_iface_dict' --type=py

Length of output: 4071


🏁 Script executed:

#!/bin/bash
sed -n '110,200p' provider/viommu/viommu_base.py

Length of output: 4517


Isolate interface cleanup failures and validate types
Wrap each modify_vm_device call in a try-except so one failure doesn’t block subsequent cleanups, and skip or error on non-dict entries in the list, e.g.:

     if cleanup_ifaces:
         # Handle both single dict and list of dicts
         if isinstance(iface_dict, list):
             for single_iface_dict in iface_dict:
+               if not isinstance(single_iface_dict, dict):
+                   test.log.warning(f"Skipping invalid iface entry: {single_iface_dict!r}")
+                   continue
+               try:
                     libvirt_vmxml.modify_vm_device(
                             vm_xml.VMXML.new_from_dumpxml(vm.name),
                             "interface", single_iface_dict)
+               except Exception as e:
+                   test.log.warning(f"Failed to cleanup interface: {e}")
         else:
             libvirt_vmxml.modify_vm_device(
                     vm_xml.VMXML.new_from_dumpxml(vm.name),
                     "interface", iface_dict)
📝 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
# Handle both single dict and list of dicts
if isinstance(iface_dict, list):
for single_iface_dict in iface_dict:
libvirt_vmxml.modify_vm_device(
vm_xml.VMXML.new_from_dumpxml(vm.name),
"interface", single_iface_dict)
else:
libvirt_vmxml.modify_vm_device(
vm_xml.VMXML.new_from_dumpxml(vm.name),
"interface", iface_dict)
# Handle both single dict and list of dicts
if isinstance(iface_dict, list):
for single_iface_dict in iface_dict:
if not isinstance(single_iface_dict, dict):
test.log.warning(f"Skipping invalid iface entry: {single_iface_dict!r}")
continue
try:
libvirt_vmxml.modify_vm_device(
vm_xml.VMXML.new_from_dumpxml(vm.name),
"interface", single_iface_dict)
except Exception as e:
test.log.warning(f"Failed to cleanup interface: {e}")
else:
libvirt_vmxml.modify_vm_device(
vm_xml.VMXML.new_from_dumpxml(vm.name),
"interface", iface_dict)
🤖 Prompt for AI Agents
In libvirt/tests/src/sriov/vIOMMU/iommu_device_settings.py around lines 61 to
70, wrap each libvirt_vmxml.modify_vm_device call in a try/except so one failing
cleanup won’t stop others, and validate input types: when iterating a list,
check isinstance(single_iface_dict, dict) and skip/log a warning for non-dict
entries, and in the non-list branch ensure iface_dict is a dict before calling
modify_vm_device; on exception catch Exception as e, log the error with details
and continue to the next item.

…ification

- Fixed VIOMMUTest to skip address attributes for hostdev interfaces to prevent conflicts
- Added proper console session cleanup to prevent hanging sessions during debugging
- Enhanced dmesg IOMMU verification to properly check for IOMMU group messages
- Added debug logging for IOMMU group messages found
- Improved error handling with clear failure messages for IOMMU verification
- All changes maintain backward compatibility and pass code quality checks
- Use remove-then-add approach for multiple interfaces to ensure clean state
- Add proper console session cleanup to prevent 'existing session' errors
- Remove all interfaces before adding new ones to avoid conflicts
- Use add_vm_device for multiple interfaces and modify_vm_device for single interface
- Note: Single interface still uses modify_vm_device to maintain existing test logic
- Consider: Future improvement could use consistent remove-then-add approach for all cases
- Use remove-then-add approach for multiple interfaces to ensure clean state
- Add proper console session cleanup to prevent 'existing session' errors
- Use correct libvirt.add_vm_device method from virttest.utils_test
- Add consistent debug logging for interface device XML
- Remove all interfaces before adding new ones to avoid conflicts
- Note: Single interface still uses modify_vm_device to maintain existing test logic
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
libvirt/tests/src/sriov/vIOMMU/iommu_device_settings.py (1)

62-75: Harden multi-interface cleanup: add per-item try/except and type validation.

One failure can block subsequent interfaces; non-dict entries would also slip through. Handle both.

         if cleanup_ifaces:
             # Handle both single dict and list of dicts
             if isinstance(iface_dict, list):
                 libvirt_vmxml.remove_vm_devices_by_type(vm, 'interface')
-                for single_iface_dict in iface_dict:
-                    dev_obj = libvirt_vmxml.create_vm_device_by_type("interface", single_iface_dict)
-                    test.log.debug(f"XML of interface device is:\n{dev_obj}")
-                    libvirt.add_vm_device(
-                            vm_xml.VMXML.new_from_dumpxml(vm.name),
-                            dev_obj)
+                for single_iface_dict in iface_dict:
+                    if not isinstance(single_iface_dict, dict):
+                        test.log.warning(f"Skipping invalid iface entry: {single_iface_dict!r}")
+                        continue
+                    try:
+                        dev_obj = libvirt_vmxml.create_vm_device_by_type("interface", single_iface_dict)
+                        test.log.debug(f"XML of interface device is:\n{dev_obj}")
+                        libvirt.add_vm_device(
+                                vm_xml.VMXML.new_from_dumpxml(vm.name),
+                                dev_obj)
+                    except Exception as e:
+                        test.log.warning(f"Failed to add interface {single_iface_dict!r}: {e}")
             else:
-                libvirt_vmxml.modify_vm_device(
-                        vm_xml.VMXML.new_from_dumpxml(vm.name),
-                        "interface", iface_dict)
+                if not isinstance(iface_dict, dict):
+                    test.log.warning(f"Skipping invalid iface_dict: {iface_dict!r}")
+                else:
+                    try:
+                        libvirt_vmxml.modify_vm_device(
+                                vm_xml.VMXML.new_from_dumpxml(vm.name),
+                                "interface", iface_dict)
+                    except Exception as e:
+                        test.log.warning(f"Failed to modify interface {iface_dict!r}: {e}")

Optional: fetch VMXML once before the loop and reuse to avoid repeated new_from_dumpxml calls.

🧹 Nitpick comments (1)
libvirt/tests/src/sriov/vIOMMU/iommu_device_settings.py (1)

6-6: Avoid shadowing libvirt binding; alias the helper import.

Using the name libvirt here can confuse with the official libvirt Python binding. Prefer aliasing.

-from virttest.utils_test import libvirt
+from virttest.utils_test import libvirt as utils_libvirt

If you adopt this, also update usages (e.g., add_vm_device calls) accordingly in this file.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f94c7e and 8c2b566.

📒 Files selected for processing (1)
  • libvirt/tests/src/sriov/vIOMMU/iommu_device_settings.py (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Python 3.11
  • GitHub Check: Python 3.9
  • GitHub Check: Python 3.12
  • GitHub Check: Python 3.8
🔇 Additional comments (3)
libvirt/tests/src/sriov/vIOMMU/iommu_device_settings.py (3)

43-43: Good: close session before destroying the VM.

Prevents stale serial handles and avoids read/write on a torn-down domain.


79-80: Serial console re-init after restart looks good.

Ensures a fresh console endpoint for the new boot.


104-107: Finally block is safe and idempotent.

Guarded vm_session close and unconditional serial console cleanup are appropriate.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
provider/viommu/viommu_base.py (1)

119-121: Address the previously flagged security and code quality issues.

Two issues remain unresolved from the previous review:

  1. Line 120: The mac_addr variable is assigned but never used.
  2. Line 121: Using eval() poses a code injection risk; use ast.literal_eval() instead.

Please refer to the detailed remediation steps in the previous review comments.

🧹 Nitpick comments (1)
provider/viommu/viommu_base.py (1)

133-149: LGTM! Consider clarifying the in-place mutation in the docstring.

The helper method correctly encapsulates the controller addressing logic. The method modifies iface_dict in place, which is appropriate for this use case.

For improved clarity, consider explicitly noting the in-place mutation in the docstring:

     def _update_iface_dict_with_controller_info(self, iface_dict):
         """
-        Update interface dictionary with controller addressing information
+        Update interface dictionary in place with controller addressing information
 
         :param iface_dict: Interface dictionary to update with controller info
         """
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c2b566 and 19b1adb.

📒 Files selected for processing (1)
  • provider/viommu/viommu_base.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
provider/viommu/viommu_base.py (1)
provider/sriov/sriov_base.py (1)
  • parse_iface_dict (173-195)
🪛 Ruff (0.13.2)
provider/viommu/viommu_base.py

120-120: Local variable mac_addr is assigned to but never used

Remove assignment to unused variable mac_addr

(F841)


121-121: Use of possibly insecure function; consider using ast.literal_eval

(S307)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Python 3.11
  • GitHub Check: Python 3.8
  • GitHub Check: Python 3.12
  • GitHub Check: Python 3.9
🔇 Additional comments (3)
provider/viommu/viommu_base.py (3)

111-111: LGTM!

The debug log addition improves observability during VM configuration backup.


113-131: LGTM! Well-structured refactoring for multi-interface support.

The refactored method correctly handles both single dict and list of dicts, maintaining backward compatibility while adding the new functionality. The extraction of controller info logic into a helper method improves maintainability.


263-263: LGTM!

The debug log addition improves observability during VM configuration restoration and pairs well with the backup log in __init__.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
provider/viommu/viommu_base.py (1)

133-149: Clarify mutation semantics and add defensive checks.

The method mutates iface_dict in-place but doesn't return a value, which can be unclear. Additionally, the guard condition on line 139 may allow unintended updates to empty dicts, and there's no validation that controller_dicts[-1] contains required keys.

Consider these improvements:

  1. Clarify docstring about in-place mutation:
     def _update_iface_dict_with_controller_info(self, iface_dict):
         """
-        Update interface dictionary with controller addressing information
+        Update interface dictionary in-place with controller addressing information
+        
+        Note: This method mutates the provided iface_dict and does not return a value.

         :param iface_dict: Interface dictionary to update with controller info
         """
  1. Add defensive checks:
-        if self.controller_dicts and iface_dict and iface_dict.get('type_name') != 'hostdev':
+        # Only update if we have a non-hostdev interface with meaningful content
+        if not self.controller_dicts or not iface_dict:
+            return
+        
+        if iface_dict.get('type_name') == 'hostdev':
+            return
+            
+        last_controller = self.controller_dicts[-1]
+        if 'index' not in last_controller:
+            self.test.log.warning("Last controller missing 'index', skipping address update")
+            return
+            
-            iface_bus = "%0#4x" % int(self.controller_dicts[-1].get("index"))
+            iface_bus = "%0#4x" % int(last_controller["index"])
             iface_attrs = {"bus": iface_bus}
-            if self.controller_dicts[-1].get("model") == "pcie-to-pci-bridge":
+            if last_controller.get("model") == "pcie-to-pci-bridge":
♻️ Duplicate comments (2)
provider/viommu/viommu_base.py (2)

119-120: Remove unused variable mac_addr.

The mac_addr variable is generated but never used. If MAC address generation is needed for interface configuration, incorporate it into the iface_dict updates; otherwise, remove this line.

Apply this diff to remove the unused variable:

-    # generate mac address, if it is needed in iface_dict
-    mac_addr = utils_net.generate_mac_address_simple()
     iface_dict = eval(self.params.get("iface_dict", "{}"))

121-121: Replace eval() with ast.literal_eval() for security.

Using eval() poses a code injection risk if params can be influenced by untrusted input. Use ast.literal_eval() to safely evaluate string literals.

Apply this diff:

+from ast import literal_eval
+
 def parse_iface_dict(self):
     """
     Parse iface_dict from params, supporting both single dict and list of dicts

     :return: The updated iface_dict or list of iface_dicts based on type of input
     """
-    iface_dict = eval(self.params.get("iface_dict", "{}"))
+    iface_dict = literal_eval(self.params.get("iface_dict", "{}"))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19b1adb and 31b0139.

📒 Files selected for processing (1)
  • provider/viommu/viommu_base.py (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
provider/viommu/viommu_base.py (1)
provider/sriov/sriov_base.py (1)
  • parse_iface_dict (173-195)
🪛 Ruff (0.13.2)
provider/viommu/viommu_base.py

120-120: Local variable mac_addr is assigned to but never used

Remove assignment to unused variable mac_addr

(F841)


121-121: Use of possibly insecure function; consider using ast.literal_eval

(S307)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Python 3.11
  • GitHub Check: Python 3.12
  • GitHub Check: Python 3.8
  • GitHub Check: Python 3.9
🔇 Additional comments (3)
provider/viommu/viommu_base.py (3)

111-111: LGTM!

The debug log appropriately documents the backup operation for troubleshooting purposes.


163-165: LGTM!

The debug logs appropriately trace the controller resolution logic, making troubleshooting easier.

Also applies to: 169-169


266-266: LGTM!

The debug log appropriately documents the restore operation, providing symmetric logging with the backup operation in __init__.

Comment on lines +123 to +131
if isinstance(iface_dict, list):
result = []
for single_iface_dict in iface_dict:
self._update_iface_dict_with_controller_info(single_iface_dict)
result.append(single_iface_dict)
return result
else:
self._update_iface_dict_with_controller_info(iface_dict)
return iface_dict
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

Add input validation and error handling.

The code assumes iface_dict is either a list or a dict but doesn't validate this. If parsing yields an unexpected type (e.g., string, int, None with truthy check bypassed), subsequent operations may fail with unclear errors.

Consider adding validation:

     iface_dict = literal_eval(self.params.get("iface_dict", "{}"))
+    
+    if not isinstance(iface_dict, (dict, list)):
+        raise ValueError(f"iface_dict must be dict or list, got {type(iface_dict).__name__}")

     if isinstance(iface_dict, list):
+        if not all(isinstance(item, dict) for item in iface_dict):
+            raise ValueError("All items in iface_dict list must be dicts")
         result = []
         for single_iface_dict in iface_dict:
             self._update_iface_dict_with_controller_info(single_iface_dict)
             result.append(single_iface_dict)
         return result
     else:
         self._update_iface_dict_with_controller_info(iface_dict)
         return iface_dict
📝 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 isinstance(iface_dict, list):
result = []
for single_iface_dict in iface_dict:
self._update_iface_dict_with_controller_info(single_iface_dict)
result.append(single_iface_dict)
return result
else:
self._update_iface_dict_with_controller_info(iface_dict)
return iface_dict
iface_dict = literal_eval(self.params.get("iface_dict", "{}"))
if not isinstance(iface_dict, (dict, list)):
raise ValueError(f"iface_dict must be dict or list, got {type(iface_dict).__name__}")
if isinstance(iface_dict, list):
if not all(isinstance(item, dict) for item in iface_dict):
raise ValueError("All items in iface_dict list must be dicts")
result = []
for single_iface_dict in iface_dict:
self._update_iface_dict_with_controller_info(single_iface_dict)
result.append(single_iface_dict)
return result
else:
self._update_iface_dict_with_controller_info(iface_dict)
return iface_dict

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
provider/viommu/viommu_base.py (1)

140-146: Add defensive checks for controller attributes.

The code assumes self.controller_dicts[-1] has an "index" key and that get_free_pci_slot succeeds, but neither is validated. Missing keys or failures could cause unclear runtime errors.

Consider adding validation:

+    last_controller = self.controller_dicts[-1]
+    if "index" not in last_controller:
+        raise KeyError(f"Controller missing 'index' key: {last_controller}")
+    
-    iface_bus = "%0#4x" % int(self.controller_dicts[-1].get("index"))
+    iface_bus = "%0#4x" % int(last_controller["index"])
     iface_attrs = {"bus": iface_bus}
-    if self.controller_dicts[-1].get("model") == "pcie-to-pci-bridge":
+    if last_controller.get("model") == "pcie-to-pci-bridge":
         iface_slot = libvirt_pcicontr.get_free_pci_slot(
                 vm_xml.VMXML.new_from_dumpxml(self.vm.name),
                 controller_bus=iface_bus)
+        if not iface_slot:
+            raise ValueError(f"No free PCI slot available on bus {iface_bus}")
         iface_attrs.update({"slot": iface_slot})
♻️ Duplicate comments (3)
provider/viommu/viommu_base.py (3)

120-120: Remove unused variable mac_addr.

The mac_addr variable is generated but never used in the method. This adds unnecessary overhead and confusion.

Apply this diff to remove the unused variable:

-    # generate mac address, if it is needed in iface_dict
-    mac_addr = utils_net.generate_mac_address_simple()
     iface_dict = eval(self.params.get("iface_dict", "{}"))

121-121: Replace eval() with ast.literal_eval() for security.

Using eval() poses a code injection risk if params can be influenced by untrusted input. ast.literal_eval() safely evaluates string literals.

Apply this diff:

+from ast import literal_eval
+
 def parse_iface_dict(self):
-    iface_dict = eval(self.params.get("iface_dict", "{}"))
+    iface_dict = literal_eval(self.params.get("iface_dict", "{}"))

123-131: Add input validation and error handling.

The code assumes iface_dict is either a list or a dict but doesn't validate this assumption. If parsing yields an unexpected type, subsequent operations may fail with unclear errors.

Consider adding validation:

     iface_dict = literal_eval(self.params.get("iface_dict", "{}"))
+    
+    if not isinstance(iface_dict, (dict, list)):
+        raise ValueError(f"iface_dict must be dict or list, got {type(iface_dict).__name__}")

     if isinstance(iface_dict, list):
+        if not all(isinstance(item, dict) for item in iface_dict):
+            raise ValueError("All items in iface_dict list must be dicts")
         result = []
         for single_iface_dict in iface_dict:
             self._update_iface_dict_with_controller_info(single_iface_dict)
             result.append(single_iface_dict)
         return result
     else:
         self._update_iface_dict_with_controller_info(iface_dict)
         return iface_dict
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31b0139 and 869b90c.

📒 Files selected for processing (1)
  • provider/viommu/viommu_base.py (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
provider/viommu/viommu_base.py (1)
provider/sriov/sriov_base.py (1)
  • parse_iface_dict (173-195)
🪛 Ruff (0.13.2)
provider/viommu/viommu_base.py

120-120: Local variable mac_addr is assigned to but never used

Remove assignment to unused variable mac_addr

(F841)


121-121: Use of possibly insecure function; consider using ast.literal_eval

(S307)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Python 3.11
  • GitHub Check: Python 3.12
  • GitHub Check: Python 3.8
  • GitHub Check: Python 3.9
🔇 Additional comments (5)
provider/viommu/viommu_base.py (5)

111-111: LGTM!

The debug log provides useful visibility into the backup operation.


163-165: LGTM!

The added debug logs improve traceability when processing controllers with pre-controller dependencies.


186-187: LGTM!

The additional debug logs provide valuable insights into controller addressing resolution.

Also applies to: 211-211


277-277: LGTM!

The debug log confirms successful restoration of the VM configuration.


166-184: Verify test coverage for parent controller lookup scenarios.
No test cases were found for:

  • Parent controller with target.busNr set
  • Parent controller without target.busNr (using index)
  • No matching parent (fallback to get_max_contr_indexes)
    Ensure tests exist for all three scenarios.

:param iface_dict: Interface dictionary to update with controller info
"""
if self.controller_dicts and iface_dict and iface_dict.get('type_name') != 'hostdev':
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 conditional check for empty dict.

The condition iface_dict will evaluate to False for an empty dict {}, potentially skipping valid updates. If an empty dict is intentionally meant to skip processing, this should be explicit.

Apply this diff to be explicit:

-    if self.controller_dicts and iface_dict and iface_dict.get('type_name') != 'hostdev':
+    if self.controller_dicts and iface_dict is not None and iface_dict.get('type_name') != 'hostdev':

Or, if empty dicts should skip processing:

-    if self.controller_dicts and iface_dict and iface_dict.get('type_name') != 'hostdev':
+    if self.controller_dicts and iface_dict and len(iface_dict) > 0 and iface_dict.get('type_name') != 'hostdev':
📝 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 self.controller_dicts and iface_dict and iface_dict.get('type_name') != 'hostdev':
if self.controller_dicts and iface_dict is not None and iface_dict.get('type_name') != 'hostdev':
🤖 Prompt for AI Agents
provider/viommu/viommu_base.py around line 139: the current conditional relies
on the truthiness of iface_dict so an empty dict will be treated as false and
unintentionally skipped; replace the truthiness check with an explicit test—if
empty dicts should be processed, change the condition to explicitly check that
iface_dict is not None before checking its type_name; if empty dicts should be
skipped, make that explicit by testing that iface_dict is not an empty dict (or
explicitly checking for existence of type_name) before comparing to 'hostdev'.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
provider/viommu/viommu_base.py (4)

119-120: Remove unused variable mac_addr.

The mac_addr variable is generated but never used in the method. If MAC address generation is needed for interface configuration, it should be incorporated into the iface_dict updates; otherwise, remove this line.

Apply this diff to remove the unused variable:

-    # generate mac address, if it is needed in iface_dict
-    mac_addr = utils_net.generate_mac_address_simple()
     iface_dict = eval(self.params.get("iface_dict", "{}"))

121-121: Replace eval() with ast.literal_eval() for security.

Using eval() to parse configuration strings poses a code injection risk if params can be influenced by untrusted input. ast.literal_eval() safely evaluates string literals containing Python data structures.

Apply this diff to use the safer alternative:

+from ast import literal_eval
+
 def parse_iface_dict(self):
     """
     Parse iface_dict from params, supporting both single dict and list of dicts

     :return: The updated iface_dict or list of iface_dicts based on type of input
     """
-    iface_dict = eval(self.params.get("iface_dict", "{}"))
+    iface_dict = literal_eval(self.params.get("iface_dict", "{}"))

123-131: Add input validation and error handling.

The code assumes iface_dict is either a list or a dict but doesn't validate this. If parsing yields an unexpected type (e.g., string, int, None with truthy check bypassed), subsequent operations may fail with unclear errors.

Consider adding validation:

     iface_dict = literal_eval(self.params.get("iface_dict", "{}"))
+    
+    if not isinstance(iface_dict, (dict, list)):
+        raise ValueError(f"iface_dict must be dict or list, got {type(iface_dict).__name__}")

     if isinstance(iface_dict, list):
+        if not all(isinstance(item, dict) for item in iface_dict):
+            raise ValueError("All items in iface_dict list must be dicts")
         result = []
         for single_iface_dict in iface_dict:
             self._update_iface_dict_with_controller_info(single_iface_dict)
             result.append(single_iface_dict)
         return result
     else:
         self._update_iface_dict_with_controller_info(iface_dict)
         return iface_dict

139-139: Fix conditional check for empty dict.

The condition iface_dict will evaluate to False for an empty dict {}, potentially skipping valid updates. If an empty dict is intentionally meant to skip processing, this should be explicit.

Apply this diff to be explicit:

-    if self.controller_dicts and iface_dict and iface_dict.get('type_name') != 'hostdev':
+    if self.controller_dicts and iface_dict is not None and iface_dict.get('type_name') != 'hostdev':

Or, if empty dicts should skip processing:

-    if self.controller_dicts and iface_dict and iface_dict.get('type_name') != 'hostdev':
+    if self.controller_dicts and iface_dict and len(iface_dict) > 0 and iface_dict.get('type_name') != 'hostdev':
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 869b90c and c771b29.

📒 Files selected for processing (1)
  • provider/viommu/viommu_base.py (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
provider/viommu/viommu_base.py (1)
provider/sriov/sriov_base.py (1)
  • parse_iface_dict (173-195)
🪛 Ruff (0.13.2)
provider/viommu/viommu_base.py

120-120: Local variable mac_addr is assigned to but never used

Remove assignment to unused variable mac_addr

(F841)


121-121: Use of possibly insecure function; consider using ast.literal_eval

(S307)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Python 3.9
  • GitHub Check: Python 3.12
  • GitHub Check: Python 3.11
  • GitHub Check: Python 3.8
🔇 Additional comments (4)
provider/viommu/viommu_base.py (4)

111-111: LGTM!

The debug log improves traceability during test setup.


163-165: LGTM!

The additional debug logging improves traceability when debugging controller configuration issues.

Also applies to: 186-186, 211-211


288-288: LGTM!

The debug log improves traceability during test teardown.


215-228: Verify busNr-based index assignment consistency
Mirrors existing parent-controller logic; no conflicting patterns found. Confirm that using target.busNr as index aligns with libvirt’s controller indexing semantics.

Comment on lines 166 to 168
parent_contrs = [(c.get("index"), c.get("target", {}).get("busNr"))
for c in self.controller_dicts
if c["type"] == contr_dict["type"] and c["model"] == pre_controller]
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

Add safe access for controller attributes.

The list comprehension accesses c["type"] and c["model"] which could raise KeyError if these keys are missing from a controller dict. Use .get() for safer access.

Apply this diff to add safe access:

-                parent_contrs = [(c.get("index"), c.get("target", {}).get("busNr")) 
-                               for c in self.controller_dicts
-                               if c["type"] == contr_dict["type"] and c["model"] == pre_controller]
+                parent_contrs = [(c.get("index"), c.get("target", {}).get("busNr")) 
+                               for c in self.controller_dicts
+                               if c.get("type") == contr_dict.get("type") and c.get("model") == pre_controller]
📝 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
parent_contrs = [(c.get("index"), c.get("target", {}).get("busNr"))
for c in self.controller_dicts
if c["type"] == contr_dict["type"] and c["model"] == pre_controller]
parent_contrs = [(c.get("index"), c.get("target", {}).get("busNr"))
for c in self.controller_dicts
if c.get("type") == contr_dict.get("type") and c.get("model") == pre_controller]
🤖 Prompt for AI Agents
In provider/viommu/viommu_base.py around lines 166 to 168, the list
comprehension uses c["type"] and c["model"] which can raise KeyError; change
those to c.get("type") and c.get("model") so missing keys return None and the
comparison is safe, preserving the existing c.get("index") and c.get("target",
{}).get("busNr") accesses; ensure the logic still filters controllers by
comparing c.get("type") == contr_dict.get("type") and c.get("model") ==
pre_controller (or contr_dict.get("model") if that was intended).

- Refactored _find_pre_controller_index into smaller, focused methods
- Added flexible lookup strategies: dict format, direct index, alias name, model name, fallback
- Implemented consistent error handling with warnings + fallback instead of exceptions
- Added controller existence validation for explicit index references
- Improved maintainability and readability of controller lookup logic
- Enhanced backward compatibility with existing configurations
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (3)
provider/viommu/viommu_base.py (3)

113-131: Previous review issues still apply; approve the list-handling approach.

The issues previously flagged for lines 120-121 (unused mac_addr, insecure eval()) and 123-131 (missing input validation) remain unaddressed. Please resolve those before merging.

The overall approach to handle both single dict and list of dicts is well-designed and maintains backward compatibility.


133-149: Previous review issue still applies; approve the method extraction.

The issue previously flagged for line 139 (empty dict truthiness check) remains unaddressed.

The extraction of this logic into a dedicated helper method improves code maintainability and supports the new multi-interface handling. The address computation logic is correct.


352-362: Previous review issue still applies for safe dictionary access.

The issue previously flagged regarding unsafe use of c["type"] and c["model"] (which could raise KeyError) remains unaddressed. These should use .get() for safer access.

Apply this diff as suggested in previous review:

         pre_contrs = list(
             filter(None, [c.get("index") for c in self.controller_dicts
-                          if c["type"] == contr_dict["type"] and
-                          c["model"] == pre_controller]))
+                          if c.get("type") == contr_dict.get("type") and
+                          c.get("model") == pre_controller]))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c771b29 and a5be1ec.

📒 Files selected for processing (1)
  • provider/viommu/viommu_base.py (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
provider/viommu/viommu_base.py (1)
provider/sriov/sriov_base.py (1)
  • parse_iface_dict (173-195)
🪛 Ruff (0.13.3)
provider/viommu/viommu_base.py

120-120: Local variable mac_addr is assigned to but never used

Remove assignment to unused variable mac_addr

(F841)


121-121: Use of possibly insecure function; consider using ast.literal_eval

(S307)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Python 3.9
  • GitHub Check: Python 3.12
  • GitHub Check: Python 3.8
  • GitHub Check: Python 3.11
🔇 Additional comments (3)
provider/viommu/viommu_base.py (3)

163-167: LGTM! Enhanced logging improves debuggability.

The additional logging statements at controller processing stages will help troubleshoot pre-controller resolution and index assignment issues.

Also applies to: 192-192


269-269: LGTM! Consistent logging for teardown.

The debug log confirms VM configuration restoration, maintaining consistency with setup logging.


271-362: LGTM! Well-designed flexible lookup mechanism.

The multi-strategy pre-controller resolution (dict format → direct index → model name → fallback) with comprehensive logging and helper method extraction is well-architected. The separation of concerns across _find_by_dict_format, _find_by_direct_index, _find_by_alias_name, and _find_by_model_name improves maintainability and testability.

Once the critical and major issues are addressed, this implementation will be robust.

Comment on lines 196 to 209
# Only assign index if it wasn't already assigned
if "index" not in contr_dict:
# Check if target busNr is specified and use it as index
if contr_dict.get("target", {}).get("busNr"):
target_bus = int(contr_dict["target"]["busNr"])
contr_dict["index"] = target_bus
self.test.log.debug(f"Using target busNr {target_bus} as index for controller {i}: {contr_dict['model']}")
else:
contr_dict["index"] = libvirt_pcicontr.get_max_contr_indexes(
vm_xml.VMXML.new_from_dumpxml(self.vm.name),
contr_dict["type"], contr_dict["model"])[-1]
self.test.log.debug(f"Assigned index {contr_dict['index']} to controller {i}: {contr_dict['model']}")
else:
self.test.log.debug(f"Index already assigned {contr_dict['index']} for controller {i}: {contr_dict['model']}")
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 truthy check that incorrectly rejects busNr=0.

Line 199 uses a truthy check on busNr, which evaluates to False when busNr is 0. Since 0 is a valid bus number, this check incorrectly skips the target busNr logic and falls through to the else branch.

Apply this diff to check explicitly for None:

-            if contr_dict.get("target", {}).get("busNr"):
+            if contr_dict.get("target", {}).get("busNr") is not None:
                 target_bus = int(contr_dict["target"]["busNr"])
📝 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
# Only assign index if it wasn't already assigned
if "index" not in contr_dict:
# Check if target busNr is specified and use it as index
if contr_dict.get("target", {}).get("busNr"):
target_bus = int(contr_dict["target"]["busNr"])
contr_dict["index"] = target_bus
self.test.log.debug(f"Using target busNr {target_bus} as index for controller {i}: {contr_dict['model']}")
else:
contr_dict["index"] = libvirt_pcicontr.get_max_contr_indexes(
vm_xml.VMXML.new_from_dumpxml(self.vm.name),
contr_dict["type"], contr_dict["model"])[-1]
self.test.log.debug(f"Assigned index {contr_dict['index']} to controller {i}: {contr_dict['model']}")
else:
self.test.log.debug(f"Index already assigned {contr_dict['index']} for controller {i}: {contr_dict['model']}")
# Only assign index if it wasn't already assigned
if "index" not in contr_dict:
# Check if target busNr is specified and use it as index
if contr_dict.get("target", {}).get("busNr") is not None:
target_bus = int(contr_dict["target"]["busNr"])
contr_dict["index"] = target_bus
self.test.log.debug(f"Using target busNr {target_bus} as index for controller {i}: {contr_dict['model']}")
else:
contr_dict["index"] = libvirt_pcicontr.get_max_contr_indexes(
vm_xml.VMXML.new_from_dumpxml(self.vm.name),
contr_dict["type"], contr_dict["model"])[-1]
self.test.log.debug(f"Assigned index {contr_dict['index']} to controller {i}: {contr_dict['model']}")
else:
self.test.log.debug(f"Index already assigned {contr_dict['index']} for controller {i}: {contr_dict['model']}")
🤖 Prompt for AI Agents
In provider/viommu/viommu_base.py around lines 196 to 209, the truthy check on
contr_dict.get("target", {}).get("busNr") incorrectly treats busNr==0 as absent;
change the logic to explicitly test for None (e.g., bus_nr =
contr_dict.get("target", {}).get("busNr"); if bus_nr is not None: target_bus =
int(bus_nr); contr_dict["index"] = target_bus; ...) so zero is accepted while
still handling missing values, and keep the debug log and remaining branches
unchanged.

Comment on lines +271 to +302
def _find_pre_controller_index(self, pre_controller, contr_dict):
"""
Find the index of the pre_controller using flexible lookup methods.
:param pre_controller: The pre_controller value to look up
:param contr_dict: The current controller dictionary
:return: The index of the pre_controller
"""
self.test.log.debug(f"Finding pre_controller: {pre_controller}")

result = None
# Try various dictionary format first (most explicit)
if isinstance(pre_controller, dict):
result = self._find_by_dict_format(pre_controller)

# Try direct index (numeric string)
if result is None and isinstance(pre_controller, str) and pre_controller.isdigit():
result = self._find_by_direct_index(pre_controller)

# Try model name (backward compatibility)
if result is None and isinstance(pre_controller, str):
result = self._find_by_model_name(pre_controller, contr_dict)

# Fallback to existing VM controllers
if result is None:
self.test.log.debug(f"Fallback: Looking for existing controller with model {pre_controller}")
result = libvirt_pcicontr.get_max_contr_indexes(
vm_xml.VMXML.new_from_dumpxml(self.vm.name),
contr_dict["type"], pre_controller)[-1]
self.test.log.debug(f"Using fallback index: {result}")

return result
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Validate non-empty list before accessing [-1] on fallback.

Line 299 accesses [-1] on the result of get_max_contr_indexes() without verifying the list is non-empty. If no matching controllers exist, this raises IndexError.

Apply this diff to add validation:

         # Fallback to existing VM controllers
         if result is None:
             self.test.log.debug(f"Fallback: Looking for existing controller with model {pre_controller}")
-            result = libvirt_pcicontr.get_max_contr_indexes(
+            indexes = libvirt_pcicontr.get_max_contr_indexes(
                 vm_xml.VMXML.new_from_dumpxml(self.vm.name),
-                contr_dict["type"], pre_controller)[-1]
+                contr_dict["type"], pre_controller)
+            if not indexes:
+                raise ValueError(f"No controllers found matching type={contr_dict['type']}, model={pre_controller}")
+            result = indexes[-1]
             self.test.log.debug(f"Using fallback index: {result}")
📝 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
def _find_pre_controller_index(self, pre_controller, contr_dict):
"""
Find the index of the pre_controller using flexible lookup methods.
:param pre_controller: The pre_controller value to look up
:param contr_dict: The current controller dictionary
:return: The index of the pre_controller
"""
self.test.log.debug(f"Finding pre_controller: {pre_controller}")
result = None
# Try various dictionary format first (most explicit)
if isinstance(pre_controller, dict):
result = self._find_by_dict_format(pre_controller)
# Try direct index (numeric string)
if result is None and isinstance(pre_controller, str) and pre_controller.isdigit():
result = self._find_by_direct_index(pre_controller)
# Try model name (backward compatibility)
if result is None and isinstance(pre_controller, str):
result = self._find_by_model_name(pre_controller, contr_dict)
# Fallback to existing VM controllers
if result is None:
self.test.log.debug(f"Fallback: Looking for existing controller with model {pre_controller}")
result = libvirt_pcicontr.get_max_contr_indexes(
vm_xml.VMXML.new_from_dumpxml(self.vm.name),
contr_dict["type"], pre_controller)[-1]
self.test.log.debug(f"Using fallback index: {result}")
return result
# Fallback to existing VM controllers
if result is None:
self.test.log.debug(f"Fallback: Looking for existing controller with model {pre_controller}")
indexes = libvirt_pcicontr.get_max_contr_indexes(
vm_xml.VMXML.new_from_dumpxml(self.vm.name),
contr_dict["type"], pre_controller)
if not indexes:
raise ValueError(f"No controllers found matching type={contr_dict['type']}, model={pre_controller}")
result = indexes[-1]
self.test.log.debug(f"Using fallback index: {result}")
🤖 Prompt for AI Agents
In provider/viommu/viommu_base.py around lines 271 to 302, the fallback path
calls libvirt_pcicontr.get_max_contr_indexes(...)[-1] without verifying the
returned list is non-empty which can raise IndexError; change this to first
assign the result to a variable, check if the list is truthy (non-empty), and
only then take the last element; if the list is empty, log a debug/warning with
context (vm name, contr_dict["type"], pre_controller) and set result to None (or
return None) so the caller safely handles the missing controller index.

Comment on lines +311 to +326
def _find_by_dict_format(self, pre_controller):
"""
Find controller by dictionary format: {'alias': 'name'} or {'index': N}
example: {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': {'index': 100,'alias': 'pci.100'}}
"""
if 'index' in pre_controller:
pre_idx = int(pre_controller['index'])
# Validate that controller exists in our controller_dicts
if self._controller_exists_by_index(pre_idx):
self.test.log.debug(f"Using explicit index from dict: {pre_idx}")
return pre_idx
elif 'alias' in pre_controller:
alias_name = pre_controller['alias']
return self._find_by_alias_name(alias_name)
self.test.log.warning(f"Invalid pre_controller dict format: {pre_controller}, or controller not found in controller_dicts. Use {{'index': N}} or {{'alias': 'name'}}, will fall back to other methods")
return None
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

Add error handling for int() conversion.

Line 317 converts pre_controller['index'] to int without validation. If the value is not a valid integer, this raises ValueError.

Apply this diff to add error handling:

         if 'index' in pre_controller:
-            pre_idx = int(pre_controller['index'])
+            try:
+                pre_idx = int(pre_controller['index'])
+            except (ValueError, TypeError) as e:
+                self.test.log.warning(f"Invalid index value in pre_controller dict: {pre_controller['index']}: {e}")
+                return None
             # Validate that controller exists in our controller_dicts
📝 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
def _find_by_dict_format(self, pre_controller):
"""
Find controller by dictionary format: {'alias': 'name'} or {'index': N}
example: {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': {'index': 100,'alias': 'pci.100'}}
"""
if 'index' in pre_controller:
pre_idx = int(pre_controller['index'])
# Validate that controller exists in our controller_dicts
if self._controller_exists_by_index(pre_idx):
self.test.log.debug(f"Using explicit index from dict: {pre_idx}")
return pre_idx
elif 'alias' in pre_controller:
alias_name = pre_controller['alias']
return self._find_by_alias_name(alias_name)
self.test.log.warning(f"Invalid pre_controller dict format: {pre_controller}, or controller not found in controller_dicts. Use {{'index': N}} or {{'alias': 'name'}}, will fall back to other methods")
return None
def _find_by_dict_format(self, pre_controller):
"""
Find controller by dictionary format: {'alias': 'name'} or {'index': N}
example: {'type': 'pci', 'model': 'pcie-root-port', 'pre_controller': {'index': 100,'alias': 'pci.100'}}
"""
if 'index' in pre_controller:
try:
pre_idx = int(pre_controller['index'])
except (ValueError, TypeError) as e:
self.test.log.warning(f"Invalid index value in pre_controller dict: {pre_controller['index']}: {e}")
return None
# Validate that controller exists in our controller_dicts
if self._controller_exists_by_index(pre_idx):
self.test.log.debug(f"Using explicit index from dict: {pre_idx}")
return pre_idx
elif 'alias' in pre_controller:
alias_name = pre_controller['alias']
return self._find_by_alias_name(alias_name)
self.test.log.warning(
f"Invalid pre_controller dict format: {pre_controller}, "
"or controller not found in controller_dicts. "
"Use {'index': N} or {'alias': 'name'}, will fall back to other methods"
)
return None
🤖 Prompt for AI Agents
In provider/viommu/viommu_base.py around lines 311 to 326, the code calls
int(pre_controller['index']) without validation which can raise ValueError (or
TypeError) for non-integer inputs; wrap the conversion in a try/except catching
ValueError and TypeError, log a clear error message including the invalid value
and pre_controller dict (use self.test.log.error or warning), and return None so
the function falls back to other lookup methods instead of raising.

Comment on lines +342 to +350
def _find_by_alias_name(self, alias_name):
"""Find controller by alias name passed in configuration """
for c in self.controller_dicts:
if c.get("alias", {}).get("name") == alias_name:
pre_idx = int(c.get("index"))
self.test.log.debug(f"Found controller by alias {alias_name}: index {pre_idx}")
return pre_idx
self.test.log.warning(f"Controller with alias '{alias_name}' not found in controller_dicts, will fall back to other methods")
return None
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

Add validation before int() conversion.

Line 346 converts c.get("index") to int without checking if it's None. If a controller's index is missing, this raises TypeError.

Apply this diff to add validation:

         for c in self.controller_dicts:
             if c.get("alias", {}).get("name") == alias_name:
-                pre_idx = int(c.get("index"))
+                index = c.get("index")
+                if index is None:
+                    self.test.log.warning(f"Controller with alias '{alias_name}' has no index")
+                    continue
+                pre_idx = int(index)
                 self.test.log.debug(f"Found controller by alias {alias_name}: index {pre_idx}")
📝 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
def _find_by_alias_name(self, alias_name):
"""Find controller by alias name passed in configuration """
for c in self.controller_dicts:
if c.get("alias", {}).get("name") == alias_name:
pre_idx = int(c.get("index"))
self.test.log.debug(f"Found controller by alias {alias_name}: index {pre_idx}")
return pre_idx
self.test.log.warning(f"Controller with alias '{alias_name}' not found in controller_dicts, will fall back to other methods")
return None
def _find_by_alias_name(self, alias_name):
"""Find controller by alias name passed in configuration """
for c in self.controller_dicts:
if c.get("alias", {}).get("name") == alias_name:
index = c.get("index")
if index is None:
self.test.log.warning(f"Controller with alias '{alias_name}' has no index")
continue
pre_idx = int(index)
self.test.log.debug(f"Found controller by alias {alias_name}: index {pre_idx}")
return pre_idx
self.test.log.warning(f"Controller with alias '{alias_name}' not found in controller_dicts, will fall back to other methods")
return None
🤖 Prompt for AI Agents
In provider/viommu/viommu_base.py around lines 342 to 350, the code calls
int(c.get("index")) without validating that c.get("index") is present and
convertible, which can raise TypeError/ValueError; update the loop to retrieve
index_raw = c.get("index"), check if index_raw is not None (and optionally try
to convert with try/except ValueError), log a warning and continue to next
controller if missing/invalid, and only set pre_idx = int(index_raw) and return
it when conversion succeeds.

@hholoubk hholoubk marked this pull request as draft October 8, 2025 13:32
@hholoubk
Copy link
Contributor Author

hholoubk commented Oct 9, 2025

backward compatibility for iommu_device_settings:

(01/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.virtio_multi_devices.vhost_on.virtio: STARTED
(01/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.virtio_multi_devices.vhost_on.virtio: PASS (139.10 s)
(02/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.virtio_multi_devices.vhost_on.smmuv3: STARTED
(02/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.virtio_multi_devices.vhost_on.smmuv3: PASS (139.63 s)
(03/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.virtio_multi_devices.vhost_off.virtio: STARTED
(03/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.virtio_multi_devices.vhost_off.virtio: PASS (139.53 s)
(04/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.virtio_multi_devices.vhost_off.smmuv3: STARTED
(04/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.virtio_multi_devices.vhost_off.smmuv3: PASS (137.98 s)
(05/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.hostdev_iface.virtio: STARTED
(05/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.hostdev_iface.virtio: PASS (160.32 s)
(06/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.scsi_controller.virtio: STARTED
(06/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.scsi_controller.virtio: PASS (138.89 s)
(07/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.scsi_controller.smmuv3: STARTED
(07/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.scsi_controller.smmuv3: PASS (139.57 s)
(08/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_to_pci_bridge_controller.virtio_non_transitional.virtio: STARTED
(08/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_to_pci_bridge_controller.virtio_non_transitional.virtio: PASS (149.82 s)
(09/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_to_pci_bridge_controller.hostdev_iface.virtio: STARTED
(09/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_to_pci_bridge_controller.hostdev_iface.virtio: PASS (166.61 s)
(10/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_root_port_from_expander_bus.virtio: STARTED
(10/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_root_port_from_expander_bus.virtio: PASS (143.25 s)
(11/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_root_port_from_expander_bus.smmuv3: STARTED
(11/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_root_port_from_expander_bus.smmuv3: PASS (141.60 s)
(12/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_downstream_port.from_pcie_root_port.virtio: STARTED
(12/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_downstream_port.from_pcie_root_port.virtio: PASS (148.81 s)
(13/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_downstream_port.from_pcie_root_port.smmuv3: STARTED
(13/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_downstream_port.from_pcie_root_port.smmuv3: PASS (148.36 s)
(14/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_downstream_port.from_expander_bus.virtio: STARTED
(14/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_downstream_port.from_expander_bus.virtio: PASS (151.88 s)
(15/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_downstream_port.from_expander_bus.smmuv3: STARTED
(15/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.pcie_downstream_port.from_expander_bus.smmuv3: PASS (151.42 s)
(16/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.multiple_vfio_pcie_root_port.virtio: STARTED
(16/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.multiple_vfio_pcie_root_port.virtio: PASS (161.65 s)
(17/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.multiple_vfio_pcie_expander_bus.virtio: STARTED
(17/17) type_specific.io-github-autotest-libvirt.vIOMMU.iommu_device_settings.multiple_vfio_pcie_expander_bus.virtio: PASS (168.79 s)
RESULTS : PASS 17 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0
JOB HTML : /var/log/avocado/job-results/job-2025-10-09T02.54-35a7a92/results.html
JOB TIME : 2537.75 s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant