File 057-virtinst-implement-NVMe-disk-target-generation.patch of Package virt-manager

Subject: virtinst: implement NVMe disk target generation
From: Pavel Hrdina phrdina@redhat.com Fri Jan 9 08:51:29 2026 +0100
Date: Sun Jan 11 18:36:09 2026 +0100:
Git: 97505a9feab1ecfd23d88dad0f90444108496f83

In libvirt NVMe disk targets are based on what linux uses for device
names.

Signed-off-by: Pavel Hrdina <phrdina@redhat.com>

--- a/tests/test_disk.py
+++ b/tests/test_disk.py
@@ -40,13 +40,30 @@ def test_disk_numtotarget():
     assert DeviceDisk.target_to_num("xvdaaa") == 26 * 26 * 1 + 26 * 1 + 0
 
     conn = utils.URIs.open_testdefault_cached()
+    guest = virtinst.Guest(conn)
     disk = virtinst.DeviceDisk(conn)
     disk.bus = "ide"
 
-    assert disk.generate_target([]) == "hda"
-    assert disk.generate_target(["hda"]) == "hdb"
-    assert disk.generate_target(["hdb", "sda"]) == "hdc"
-    assert disk.generate_target(["hda", "hdd"]) == "hdb"
+    assert disk.generate_target([], guest) == "hda"
+    assert disk.generate_target(["hda"], guest) == "hdb"
+    assert disk.generate_target(["hdb", "sda"], guest) == "hdc"
+    assert disk.generate_target(["hda", "hdd"], guest) == "hdb"
+
+    disk.bus = "nvme"
+    disk.serial = "0"
+
+    assert disk.generate_target([], guest) == "nvme0n1"
+
+    controller = virtinst.DeviceController(conn)
+    controller.type = "nvme"
+    controller.serial = "0"
+    controller.index = 0
+    guest.add_device(controller)
+
+    assert disk.generate_target([], guest) == "nvme0n1"
+
+    controller.index = 1
+    assert disk.generate_target(["nvme1n1"], guest) == "nvme1n2"
 
 
 def test_disk_dir_searchable(monkeypatch):
--- a/virtManager/addhardware.py
+++ b/virtManager/addhardware.py
@@ -1453,7 +1453,7 @@ class vmmAddHardware(vmmGObjectUI):
                 used.append(d.target)
 
         self._set_disk_controller(disk)
-        disk.generate_target(used)
+        disk.generate_target(used, self.vm.xmlobj)
         return disk
 
     def _build_network(self):
--- a/virtinst/devices/disk.py
+++ b/virtinst/devices/disk.py
@@ -914,6 +914,19 @@ class DeviceDisk(Device):
         if path:
             self._set_xmlpath(path)
 
+    def get_nvme_namespace(self, guest):
+        """
+        Returns the NVMe namespace for disk based on it's bus and serial.
+
+        If no NVMe controller is defined return 0 as that is default used by
+        libvirt and virt-manager.
+        """
+        for c in guest.devices.controller:
+            if c.type == "nvme" and c.serial == self.serial:
+                return c.index
+
+        return 0
+
     def get_target_prefix(self):
         """
         Returns the suggested disk target prefix (hd, xvd, sd ...) for the
@@ -930,6 +943,7 @@ class DeviceDisk(Device):
                 "fd": 2,
                 "hd": 4,
                 "sd": 1024,
+                "nvme": 1024,
             }
             return prefix, nummap[prefix]
 
@@ -941,10 +955,12 @@ class DeviceDisk(Device):
             return _return("fd")
         elif self.bus == "ide":
             return _return("hd")
+        elif self.bus == "nvme":
+            return _return("nvme")
         # sata, scsi, usb, sd
         return _return("sd")
 
-    def generate_target(self, skip_targets):
+    def generate_target(self, skip_targets, guest):
         """
         Generate target device ('hda', 'sdb', etc..) for disk, excluding
         any targets in 'skip_targets'.
@@ -957,11 +973,17 @@ class DeviceDisk(Device):
         skip_targets = [t for t in skip_targets if t and t.startswith(prefix)]
         skip_targets.sort()
 
+        if self.bus == "nvme":
+            nvmen = self.get_nvme_namespace(guest)
+
         def get_target():
             first_found = None
 
             for i in range(maxnode):
-                gen_t = prefix + self.num_to_target(i + 1)
+                if self.bus == "nvme":
+                    gen_t = f"{prefix}{nvmen}n{i + 1}"
+                else:
+                    gen_t = prefix + self.num_to_target(i + 1)
                 if gen_t in skip_targets:
                     skip_targets.remove(gen_t)
                     continue
@@ -1008,7 +1030,7 @@ class DeviceDisk(Device):
             used.remove(self.target)
 
         self.target = None
-        self.generate_target(used)
+        self.generate_target(used, guest)
 
     #########################
     # set_defaults handling #
@@ -1078,4 +1100,4 @@ class DeviceDisk(Device):
 
         if not self.target:
             used_targets = [d.target for d in guest.devices.disk if d.target]
-            self.generate_target(used_targets)
+            self.generate_target(used_targets, guest)
--- a/virtinst/guest.py
+++ b/virtinst/guest.py
@@ -877,7 +877,7 @@ class Guest(XMLBuilder):
             if dev.DEVICE_TYPE == "disk" and dev.bus == "ide":
                 dev.bus = "sata"
                 used_targets = [d.target for d in self.devices.disk if d.target]
-                dev.generate_target(used_targets)
+                dev.generate_target(used_targets, self)
                 dev.address.clear()
 
             if dev.address.type == "pci":
openSUSE Build Service is sponsored by