Vojtech Trefny 65e9f9
From 9383855c8a15e6d7c4033cd8d7ae8310b462d166 Mon Sep 17 00:00:00 2001
Vojtech Trefny 65e9f9
From: Vojtech Trefny <vtrefny@redhat.com>
Vojtech Trefny 65e9f9
Date: Tue, 18 Oct 2022 10:38:00 +0200
Vojtech Trefny 65e9f9
Subject: [PATCH 1/3] Add a basic support for NVMe and NVMe Fabrics devices
Vojtech Trefny 65e9f9
Vojtech Trefny 65e9f9
This adds two new device types: NVMeNamespaceDevice and
Vojtech Trefny 65e9f9
NVMeFabricsNamespaceDevice mostly to allow to differentiate
Vojtech Trefny 65e9f9
between "local" and "remote" NVMe devices. The new libblockdev
Vojtech Trefny 65e9f9
NVMe plugin is required for full functionality.
Vojtech Trefny 65e9f9
---
Vojtech Trefny 65e9f9
 blivet/__init__.py                   |   6 +-
Vojtech Trefny 65e9f9
 blivet/devices/__init__.py           |   2 +-
Vojtech Trefny 65e9f9
 blivet/devices/disk.py               | 101 ++++++++++++++++++++++
Vojtech Trefny 65e9f9
 blivet/devices/lib.py                |   1 +
Vojtech Trefny 65e9f9
 blivet/populator/helpers/__init__.py |   2 +-
Vojtech Trefny 65e9f9
 blivet/populator/helpers/disk.py     |  64 ++++++++++++++
Vojtech Trefny 65e9f9
 blivet/udev.py                       |  33 +++++++
Vojtech Trefny 65e9f9
 blivet/util.py                       |   9 ++
Vojtech Trefny 65e9f9
 tests/unit_tests/populator_test.py   | 124 +++++++++++++++++++++++++++
Vojtech Trefny 65e9f9
 9 files changed, 339 insertions(+), 3 deletions(-)
Vojtech Trefny 65e9f9
Vojtech Trefny 65e9f9
diff --git a/blivet/__init__.py b/blivet/__init__.py
Vojtech Trefny 65e9f9
index bbc7ea3a..3b9e659e 100644
Vojtech Trefny 65e9f9
--- a/blivet/__init__.py
Vojtech Trefny 65e9f9
+++ b/blivet/__init__.py
Vojtech Trefny 65e9f9
@@ -67,6 +67,10 @@ if arch.is_s390():
Vojtech Trefny 65e9f9
 else:
Vojtech Trefny 65e9f9
     _REQUESTED_PLUGIN_NAMES = set(("swap", "crypto", "loop", "mdraid", "mpath", "dm", "nvdimm"))
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
+# nvme plugin is not generally available
Vojtech Trefny 65e9f9
+if hasattr(blockdev.Plugin, "NVME"):
Vojtech Trefny 65e9f9
+    _REQUESTED_PLUGIN_NAMES.add("nvme")
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
 _requested_plugins = blockdev.plugin_specs_from_names(_REQUESTED_PLUGIN_NAMES)
Vojtech Trefny 65e9f9
 # XXX force non-dbus LVM plugin
Vojtech Trefny 65e9f9
 lvm_plugin = blockdev.PluginSpec()
Vojtech Trefny 65e9f9
@@ -74,7 +78,7 @@ lvm_plugin.name = blockdev.Plugin.LVM
Vojtech Trefny 65e9f9
 lvm_plugin.so_name = "libbd_lvm.so.2"
Vojtech Trefny 65e9f9
 _requested_plugins.append(lvm_plugin)
Vojtech Trefny 65e9f9
 try:
Vojtech Trefny 65e9f9
-    # do not check for dependencies during libblockdev initializtion, do runtime
Vojtech Trefny 65e9f9
+    # do not check for dependencies during libblockdev initialization, do runtime
Vojtech Trefny 65e9f9
     # checks instead
Vojtech Trefny 65e9f9
     blockdev.switch_init_checks(False)
Vojtech Trefny 65e9f9
     succ_, avail_plugs = blockdev.try_reinit(require_plugins=_requested_plugins, reload=False, log_func=log_bd_message)
Vojtech Trefny 65e9f9
diff --git a/blivet/devices/__init__.py b/blivet/devices/__init__.py
Vojtech Trefny 65e9f9
index 8bb0a979..4d16466e 100644
Vojtech Trefny 65e9f9
--- a/blivet/devices/__init__.py
Vojtech Trefny 65e9f9
+++ b/blivet/devices/__init__.py
Vojtech Trefny 65e9f9
@@ -22,7 +22,7 @@
Vojtech Trefny 65e9f9
 from .lib import device_path_to_name, device_name_to_disk_by_path, ParentList
Vojtech Trefny 65e9f9
 from .device import Device
Vojtech Trefny 65e9f9
 from .storage import StorageDevice
Vojtech Trefny 65e9f9
-from .disk import DiskDevice, DiskFile, DMRaidArrayDevice, MultipathDevice, iScsiDiskDevice, FcoeDiskDevice, DASDDevice, ZFCPDiskDevice, NVDIMMNamespaceDevice
Vojtech Trefny 65e9f9
+from .disk import DiskDevice, DiskFile, DMRaidArrayDevice, MultipathDevice, iScsiDiskDevice, FcoeDiskDevice, DASDDevice, ZFCPDiskDevice, NVDIMMNamespaceDevice, NVMeNamespaceDevice, NVMeFabricsNamespaceDevice
Vojtech Trefny 65e9f9
 from .partition import PartitionDevice
Vojtech Trefny 65e9f9
 from .dm import DMDevice, DMLinearDevice, DMCryptDevice, DMIntegrityDevice, DM_MAJORS
Vojtech Trefny 65e9f9
 from .luks import LUKSDevice, IntegrityDevice
Vojtech Trefny 65e9f9
diff --git a/blivet/devices/disk.py b/blivet/devices/disk.py
Vojtech Trefny 65e9f9
index bc4a1b5e..b5e25939 100644
Vojtech Trefny 65e9f9
--- a/blivet/devices/disk.py
Vojtech Trefny 65e9f9
+++ b/blivet/devices/disk.py
Vojtech Trefny 65e9f9
@@ -22,10 +22,13 @@
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 import gi
Vojtech Trefny 65e9f9
 gi.require_version("BlockDev", "2.0")
Vojtech Trefny 65e9f9
+gi.require_version("GLib", "2.0")
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 from gi.repository import BlockDev as blockdev
Vojtech Trefny 65e9f9
+from gi.repository import GLib
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 import os
Vojtech Trefny 65e9f9
+from collections import namedtuple
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 from .. import errors
Vojtech Trefny 65e9f9
 from .. import util
Vojtech Trefny 65e9f9
@@ -725,3 +728,101 @@ class NVDIMMNamespaceDevice(DiskDevice):
Vojtech Trefny 65e9f9
     @property
Vojtech Trefny 65e9f9
     def sector_size(self):
Vojtech Trefny 65e9f9
         return self._sector_size
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+NVMeController = namedtuple("NVMeController", ["name", "serial", "nvme_ver", "id", "subsysnqn"])
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+class NVMeNamespaceDevice(DiskDevice):
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    """ NVMe namespace """
Vojtech Trefny 65e9f9
+    _type = "nvme"
Vojtech Trefny 65e9f9
+    _packages = ["nvme-cli"]
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    def __init__(self, device, **kwargs):
Vojtech Trefny 65e9f9
+        """
Vojtech Trefny 65e9f9
+            :param name: the device name (generally a device node's basename)
Vojtech Trefny 65e9f9
+            :type name: str
Vojtech Trefny 65e9f9
+            :keyword exists: does this device exist?
Vojtech Trefny 65e9f9
+            :type exists: bool
Vojtech Trefny 65e9f9
+            :keyword size: the device's size
Vojtech Trefny 65e9f9
+            :type size: :class:`~.size.Size`
Vojtech Trefny 65e9f9
+            :keyword parents: a list of parent devices
Vojtech Trefny 65e9f9
+            :type parents: list of :class:`StorageDevice`
Vojtech Trefny 65e9f9
+            :keyword format: this device's formatting
Vojtech Trefny 65e9f9
+            :type format: :class:`~.formats.DeviceFormat` or a subclass of it
Vojtech Trefny 65e9f9
+            :keyword nsid: namespace ID
Vojtech Trefny 65e9f9
+            :type nsid: int
Vojtech Trefny 65e9f9
+        """
Vojtech Trefny 65e9f9
+        self.nsid = kwargs.pop("nsid", 0)
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        DiskDevice.__init__(self, device, **kwargs)
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        self._clear_local_tags()
Vojtech Trefny 65e9f9
+        self.tags.add(Tags.local)
Vojtech Trefny 65e9f9
+        self.tags.add(Tags.nvme)
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        self._controllers = None
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    @property
Vojtech Trefny 65e9f9
+    def controllers(self):
Vojtech Trefny 65e9f9
+        if self._controllers is not None:
Vojtech Trefny 65e9f9
+            return self._controllers
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        self._controllers = []
Vojtech Trefny 65e9f9
+        if not hasattr(blockdev.Plugin, "NVME"):
Vojtech Trefny 65e9f9
+            # the nvme plugin is not generally available
Vojtech Trefny 65e9f9
+            log.debug("Failed to get controllers for %s: libblockdev NVME plugin is not available", self.name)
Vojtech Trefny 65e9f9
+            return self._controllers
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        try:
Vojtech Trefny 65e9f9
+            controllers = blockdev.nvme_find_ctrls_for_ns(self.sysfs_path)
Vojtech Trefny 65e9f9
+        except GLib.GError as err:
Vojtech Trefny 65e9f9
+            log.debug("Failed to get controllers for %s: %s", self.name, str(err))
Vojtech Trefny 65e9f9
+            return self._controllers
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        for controller in controllers:
Vojtech Trefny 65e9f9
+            try:
Vojtech Trefny 65e9f9
+                cpath = util.get_path_by_sysfs_path(controller, "char")
Vojtech Trefny 65e9f9
+            except RuntimeError as err:
Vojtech Trefny 65e9f9
+                log.debug("Failed to find controller %s: %s", controller, str(err))
Vojtech Trefny 65e9f9
+                continue
Vojtech Trefny 65e9f9
+            try:
Vojtech Trefny 65e9f9
+                cinfo = blockdev.nvme_get_controller_info(cpath)
Vojtech Trefny 65e9f9
+            except GLib.GError as err:
Vojtech Trefny 65e9f9
+                log.debug("Failed to get controller info for %s: %s", cpath, str(err))
Vojtech Trefny 65e9f9
+                continue
Vojtech Trefny 65e9f9
+            self._controllers.append(NVMeController(name=os.path.basename(cpath),
Vojtech Trefny 65e9f9
+                                                    serial=cinfo.serial_number,
Vojtech Trefny 65e9f9
+                                                    nvme_ver=cinfo.nvme_ver,
Vojtech Trefny 65e9f9
+                                                    id=cinfo.ctrl_id,
Vojtech Trefny 65e9f9
+                                                    subsysnqn=cinfo.subsysnqn))
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        return self._controllers
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+class NVMeFabricsNamespaceDevice(NVMeNamespaceDevice, NetworkStorageDevice):
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    """ NVMe fabrics namespace """
Vojtech Trefny 65e9f9
+    _type = "nvme-fabrics"
Vojtech Trefny 65e9f9
+    _packages = ["nvme-cli"]
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    def __init__(self, device, **kwargs):
Vojtech Trefny 65e9f9
+        """
Vojtech Trefny 65e9f9
+            :param name: the device name (generally a device node's basename)
Vojtech Trefny 65e9f9
+            :type name: str
Vojtech Trefny 65e9f9
+            :keyword exists: does this device exist?
Vojtech Trefny 65e9f9
+            :type exists: bool
Vojtech Trefny 65e9f9
+            :keyword size: the device's size
Vojtech Trefny 65e9f9
+            :type size: :class:`~.size.Size`
Vojtech Trefny 65e9f9
+            :keyword parents: a list of parent devices
Vojtech Trefny 65e9f9
+            :type parents: list of :class:`StorageDevice`
Vojtech Trefny 65e9f9
+            :keyword format: this device's formatting
Vojtech Trefny 65e9f9
+            :type format: :class:`~.formats.DeviceFormat` or a subclass of it
Vojtech Trefny 65e9f9
+        """
Vojtech Trefny 65e9f9
+        NVMeNamespaceDevice.__init__(self, device, **kwargs)
Vojtech Trefny 65e9f9
+        NetworkStorageDevice.__init__(self)
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        self._clear_local_tags()
Vojtech Trefny 65e9f9
+        self.tags.add(Tags.remote)
Vojtech Trefny 65e9f9
+        self.tags.add(Tags.nvme)
Vojtech Trefny 65e9f9
diff --git a/blivet/devices/lib.py b/blivet/devices/lib.py
Vojtech Trefny 65e9f9
index 1bda0bab..b3c4c5b0 100644
Vojtech Trefny 65e9f9
--- a/blivet/devices/lib.py
Vojtech Trefny 65e9f9
+++ b/blivet/devices/lib.py
Vojtech Trefny 65e9f9
@@ -32,6 +32,7 @@ class Tags(str, Enum):
Vojtech Trefny 65e9f9
     """Tags that describe various classes of disk."""
Vojtech Trefny 65e9f9
     local = 'local'
Vojtech Trefny 65e9f9
     nvdimm = 'nvdimm'
Vojtech Trefny 65e9f9
+    nvme = 'nvme'
Vojtech Trefny 65e9f9
     remote = 'remote'
Vojtech Trefny 65e9f9
     removable = 'removable'
Vojtech Trefny 65e9f9
     ssd = 'ssd'
Vojtech Trefny 65e9f9
diff --git a/blivet/populator/helpers/__init__.py b/blivet/populator/helpers/__init__.py
Vojtech Trefny 65e9f9
index c5ac412f..50ab4de8 100644
Vojtech Trefny 65e9f9
--- a/blivet/populator/helpers/__init__.py
Vojtech Trefny 65e9f9
+++ b/blivet/populator/helpers/__init__.py
Vojtech Trefny 65e9f9
@@ -6,7 +6,7 @@ from .formatpopulator import FormatPopulator
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 from .btrfs import BTRFSFormatPopulator
Vojtech Trefny 65e9f9
 from .boot import AppleBootFormatPopulator, EFIFormatPopulator, MacEFIFormatPopulator
Vojtech Trefny 65e9f9
-from .disk import DiskDevicePopulator, iScsiDevicePopulator, FCoEDevicePopulator, MDBiosRaidDevicePopulator, DASDDevicePopulator, ZFCPDevicePopulator, NVDIMMNamespaceDevicePopulator
Vojtech Trefny 65e9f9
+from .disk import DiskDevicePopulator, iScsiDevicePopulator, FCoEDevicePopulator, MDBiosRaidDevicePopulator, DASDDevicePopulator, ZFCPDevicePopulator, NVDIMMNamespaceDevicePopulator, NVMeNamespaceDevicePopulator, NVMeFabricsNamespaceDevicePopulator
Vojtech Trefny 65e9f9
 from .disklabel import DiskLabelFormatPopulator
Vojtech Trefny 65e9f9
 from .dm import DMDevicePopulator
Vojtech Trefny 65e9f9
 from .dmraid import DMRaidFormatPopulator
Vojtech Trefny 65e9f9
diff --git a/blivet/populator/helpers/disk.py b/blivet/populator/helpers/disk.py
Vojtech Trefny 65e9f9
index 9db7b810..9ed1eebe 100644
Vojtech Trefny 65e9f9
--- a/blivet/populator/helpers/disk.py
Vojtech Trefny 65e9f9
+++ b/blivet/populator/helpers/disk.py
Vojtech Trefny 65e9f9
@@ -22,13 +22,16 @@
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 import gi
Vojtech Trefny 65e9f9
 gi.require_version("BlockDev", "2.0")
Vojtech Trefny 65e9f9
+gi.require_version("GLib", "2.0")
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 from gi.repository import BlockDev as blockdev
Vojtech Trefny 65e9f9
+from gi.repository import GLib
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 from ... import udev
Vojtech Trefny 65e9f9
 from ... import util
Vojtech Trefny 65e9f9
 from ...devices import DASDDevice, DiskDevice, FcoeDiskDevice, iScsiDiskDevice
Vojtech Trefny 65e9f9
 from ...devices import MDBiosRaidArrayDevice, ZFCPDiskDevice, NVDIMMNamespaceDevice
Vojtech Trefny 65e9f9
+from ...devices import NVMeNamespaceDevice, NVMeFabricsNamespaceDevice
Vojtech Trefny 65e9f9
 from ...devices import device_path_to_name
Vojtech Trefny 65e9f9
 from ...storage_log import log_method_call
Vojtech Trefny 65e9f9
 from .devicepopulator import DevicePopulator
Vojtech Trefny 65e9f9
@@ -251,3 +254,64 @@ class NVDIMMNamespaceDevicePopulator(DiskDevicePopulator):
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
         log.info("%s is an NVDIMM namespace device", udev.device_get_name(self.data))
Vojtech Trefny 65e9f9
         return kwargs
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+class NVMeNamespaceDevicePopulator(DiskDevicePopulator):
Vojtech Trefny 65e9f9
+    priority = 20
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    _device_class = NVMeNamespaceDevice
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    @classmethod
Vojtech Trefny 65e9f9
+    def match(cls, data):
Vojtech Trefny 65e9f9
+        return (super(NVMeNamespaceDevicePopulator, NVMeNamespaceDevicePopulator).match(data) and
Vojtech Trefny 65e9f9
+                udev.device_is_nvme_namespace(data) and not udev.device_is_nvme_fabrics(data))
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    def _get_kwargs(self):
Vojtech Trefny 65e9f9
+        kwargs = super(NVMeNamespaceDevicePopulator, self)._get_kwargs()
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        log.info("%s is an NVMe local namespace device", udev.device_get_name(self.data))
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        if not hasattr(blockdev.Plugin, "NVME"):
Vojtech Trefny 65e9f9
+            # the nvme plugin is not generally available
Vojtech Trefny 65e9f9
+            return kwargs
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        path = udev.device_get_devname(self.data)
Vojtech Trefny 65e9f9
+        try:
Vojtech Trefny 65e9f9
+            ninfo = blockdev.nvme_get_namespace_info(path)
Vojtech Trefny 65e9f9
+        except GLib.GError as err:
Vojtech Trefny 65e9f9
+            log.debug("Failed to get namespace info for %s: %s", path, str(err))
Vojtech Trefny 65e9f9
+        else:
Vojtech Trefny 65e9f9
+            kwargs["nsid"] = ninfo.nsid
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        log.info("%s is an NVMe local namespace device", udev.device_get_name(self.data))
Vojtech Trefny 65e9f9
+        return kwargs
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+class NVMeFabricsNamespaceDevicePopulator(DiskDevicePopulator):
Vojtech Trefny 65e9f9
+    priority = 20
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    _device_class = NVMeFabricsNamespaceDevice
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    @classmethod
Vojtech Trefny 65e9f9
+    def match(cls, data):
Vojtech Trefny 65e9f9
+        return (super(NVMeFabricsNamespaceDevicePopulator, NVMeFabricsNamespaceDevicePopulator).match(data) and
Vojtech Trefny 65e9f9
+                udev.device_is_nvme_namespace(data) and udev.device_is_nvme_fabrics(data))
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    def _get_kwargs(self):
Vojtech Trefny 65e9f9
+        kwargs = super(NVMeFabricsNamespaceDevicePopulator, self)._get_kwargs()
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        log.info("%s is an NVMe fabrics namespace device", udev.device_get_name(self.data))
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        if not hasattr(blockdev.Plugin, "NVME"):
Vojtech Trefny 65e9f9
+            # the nvme plugin is not generally available
Vojtech Trefny 65e9f9
+            return kwargs
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        path = udev.device_get_devname(self.data)
Vojtech Trefny 65e9f9
+        try:
Vojtech Trefny 65e9f9
+            ninfo = blockdev.nvme_get_namespace_info(path)
Vojtech Trefny 65e9f9
+        except GLib.GError as err:
Vojtech Trefny 65e9f9
+            log.debug("Failed to get namespace info for %s: %s", path, str(err))
Vojtech Trefny 65e9f9
+        else:
Vojtech Trefny 65e9f9
+            kwargs["nsid"] = ninfo.nsid
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        return kwargs
Vojtech Trefny 65e9f9
diff --git a/blivet/udev.py b/blivet/udev.py
Vojtech Trefny 65e9f9
index efbc53d6..533a1edc 100644
Vojtech Trefny 65e9f9
--- a/blivet/udev.py
Vojtech Trefny 65e9f9
+++ b/blivet/udev.py
Vojtech Trefny 65e9f9
@@ -1023,6 +1023,39 @@ def device_is_nvdimm_namespace(info):
Vojtech Trefny 65e9f9
     return ninfo is not None
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
+def device_is_nvme_namespace(info):
Vojtech Trefny 65e9f9
+    if info.get("DEVTYPE") != "disk":
Vojtech Trefny 65e9f9
+        return False
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    if not info.get("SYS_PATH"):
Vojtech Trefny 65e9f9
+        return False
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    device = pyudev.Devices.from_sys_path(global_udev, info.get("SYS_PATH"))
Vojtech Trefny 65e9f9
+    while device:
Vojtech Trefny 65e9f9
+        if device.subsystem and device.subsystem.startswith("nvme"):
Vojtech Trefny 65e9f9
+            return True
Vojtech Trefny 65e9f9
+        device = device.parent
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    return False
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+def device_is_nvme_fabrics(info):
Vojtech Trefny 65e9f9
+    if not device_is_nvme_namespace(info):
Vojtech Trefny 65e9f9
+        return False
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    if not hasattr(blockdev.Plugin, "NVME") or not blockdev.is_plugin_available(blockdev.Plugin.NVME):  # pylint: disable=no-member
Vojtech Trefny 65e9f9
+        # nvme plugin is not available -- even if this is an nvme fabrics device we
Vojtech Trefny 65e9f9
+        # don't have tools to work with it, so we should pretend it's just a normal nvme
Vojtech Trefny 65e9f9
+        return False
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    controllers = blockdev.nvme_find_ctrls_for_ns(info.get("SYS_PATH", ""))
Vojtech Trefny 65e9f9
+    if not controllers:
Vojtech Trefny 65e9f9
+        return False
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    transport = util.get_sysfs_attr(controllers[0], "transport")
Vojtech Trefny 65e9f9
+    return transport in ("rdma", "fc", "tcp", "loop")
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
 def device_is_hidden(info):
Vojtech Trefny 65e9f9
     sysfs_path = device_get_sysfs_path(info)
Vojtech Trefny 65e9f9
     hidden = util.get_sysfs_attr(sysfs_path, "hidden")
Vojtech Trefny 65e9f9
diff --git a/blivet/util.py b/blivet/util.py
Vojtech Trefny 65e9f9
index 0e578aea..3040ee5a 100644
Vojtech Trefny 65e9f9
--- a/blivet/util.py
Vojtech Trefny 65e9f9
+++ b/blivet/util.py
Vojtech Trefny 65e9f9
@@ -432,6 +432,15 @@ def get_sysfs_path_by_name(dev_node, class_name="block"):
Vojtech Trefny 65e9f9
                            "for '%s' (it is not at '%s')" % (dev_node, dev_path))
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
+def get_path_by_sysfs_path(sysfs_path, dev_type="block"):
Vojtech Trefny 65e9f9
+    """ Return device path for a given device sysfs path. """
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    dev = get_sysfs_attr(sysfs_path, "dev")
Vojtech Trefny 65e9f9
+    if not dev or not os.path.exists("/dev/%s/%s" % (dev_type, dev)):
Vojtech Trefny 65e9f9
+        raise RuntimeError("get_path_by_sysfs_path: Could not find device for %s" % sysfs_path)
Vojtech Trefny 65e9f9
+    return os.path.realpath("/dev/%s/%s" % (dev_type, dev))
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
 def get_cow_sysfs_path(dev_path, dev_sysfsPath):
Vojtech Trefny 65e9f9
     """ Return sysfs path of cow device for a given device.
Vojtech Trefny 65e9f9
     """
Vojtech Trefny 65e9f9
diff --git a/tests/unit_tests/populator_test.py b/tests/unit_tests/populator_test.py
Vojtech Trefny 65e9f9
index 369fe878..1ee29b57 100644
Vojtech Trefny 65e9f9
--- a/tests/unit_tests/populator_test.py
Vojtech Trefny 65e9f9
+++ b/tests/unit_tests/populator_test.py
Vojtech Trefny 65e9f9
@@ -13,6 +13,7 @@ from gi.repository import BlockDev as blockdev
Vojtech Trefny 65e9f9
 from blivet.devices import DiskDevice, DMDevice, FileDevice, LoopDevice
Vojtech Trefny 65e9f9
 from blivet.devices import MDRaidArrayDevice, MultipathDevice, OpticalDevice
Vojtech Trefny 65e9f9
 from blivet.devices import PartitionDevice, StorageDevice, NVDIMMNamespaceDevice
Vojtech Trefny 65e9f9
+from blivet.devices import NVMeNamespaceDevice, NVMeFabricsNamespaceDevice
Vojtech Trefny 65e9f9
 from blivet.devicelibs import lvm
Vojtech Trefny 65e9f9
 from blivet.devicetree import DeviceTree
Vojtech Trefny 65e9f9
 from blivet.formats import get_device_format_class, get_format, DeviceFormat
Vojtech Trefny 65e9f9
@@ -21,6 +22,7 @@ from blivet.populator.helpers import DiskDevicePopulator, DMDevicePopulator, Loo
Vojtech Trefny 65e9f9
 from blivet.populator.helpers import LVMDevicePopulator, MDDevicePopulator, MultipathDevicePopulator
Vojtech Trefny 65e9f9
 from blivet.populator.helpers import OpticalDevicePopulator, PartitionDevicePopulator
Vojtech Trefny 65e9f9
 from blivet.populator.helpers import LVMFormatPopulator, MDFormatPopulator, NVDIMMNamespaceDevicePopulator
Vojtech Trefny 65e9f9
+from blivet.populator.helpers import NVMeNamespaceDevicePopulator, NVMeFabricsNamespaceDevicePopulator
Vojtech Trefny 65e9f9
 from blivet.populator.helpers import get_format_helper, get_device_helper
Vojtech Trefny 65e9f9
 from blivet.populator.helpers.boot import AppleBootFormatPopulator, EFIFormatPopulator, MacEFIFormatPopulator
Vojtech Trefny 65e9f9
 from blivet.populator.helpers.formatpopulator import FormatPopulator
Vojtech Trefny 65e9f9
@@ -591,6 +593,128 @@ class NVDIMMNamespaceDevicePopulatorTestCase(PopulatorHelperTestCase):
Vojtech Trefny 65e9f9
         self.assertTrue(device in devicetree.devices)
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
+class NVMeNamespaceDevicePopulatorTestCase(PopulatorHelperTestCase):
Vojtech Trefny 65e9f9
+    helper_class = NVMeNamespaceDevicePopulator
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    @patch("os.path.join")
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_cdrom", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_dm", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_loop", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_md", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_partition", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_disk", return_value=True)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_nvme_fabrics", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_nvme_namespace", return_value=True)
Vojtech Trefny 65e9f9
+    def test_match(self, *args):
Vojtech Trefny 65e9f9
+        """Test matching of NVMe namespace device populator."""
Vojtech Trefny 65e9f9
+        device_is_nvme_namespace = args[0]
Vojtech Trefny 65e9f9
+        self.assertTrue(self.helper_class.match(None))
Vojtech Trefny 65e9f9
+        device_is_nvme_namespace.return_value = False
Vojtech Trefny 65e9f9
+        self.assertFalse(self.helper_class.match(None))
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    @patch("os.path.join")
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_cdrom", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_dm", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_loop", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_md", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_partition", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_disk", return_value=True)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_nvme_fabrics", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_nvme_namespace", return_value=True)
Vojtech Trefny 65e9f9
+    def test_get_helper(self, *args):
Vojtech Trefny 65e9f9
+        """Test get_device_helper for NVMe namespaces."""
Vojtech Trefny 65e9f9
+        device_is_nvme_namespace = args[0]
Vojtech Trefny 65e9f9
+        data = {}
Vojtech Trefny 65e9f9
+        self.assertEqual(get_device_helper(data), self.helper_class)
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        # verify that setting one of the required True return values to False prevents success
Vojtech Trefny 65e9f9
+        device_is_nvme_namespace.return_value = False
Vojtech Trefny 65e9f9
+        self.assertNotEqual(get_device_helper(data), self.helper_class)
Vojtech Trefny 65e9f9
+        device_is_nvme_namespace.return_value = True
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_get_name")
Vojtech Trefny 65e9f9
+    def test_run(self, *args):
Vojtech Trefny 65e9f9
+        """Test disk device populator."""
Vojtech Trefny 65e9f9
+        device_get_name = args[0]
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        devicetree = DeviceTree()
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        # set up some fake udev data to verify handling of specific entries
Vojtech Trefny 65e9f9
+        data = {'SYS_PATH': 'dummy', 'DEVNAME': 'dummy', 'ID_PATH': 'dummy'}
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        device_name = "nop"
Vojtech Trefny 65e9f9
+        device_get_name.return_value = device_name
Vojtech Trefny 65e9f9
+        helper = self.helper_class(devicetree, data)
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        device = helper.run()
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        self.assertIsInstance(device, NVMeNamespaceDevice)
Vojtech Trefny 65e9f9
+        self.assertTrue(device.exists)
Vojtech Trefny 65e9f9
+        self.assertTrue(device.is_disk)
Vojtech Trefny 65e9f9
+        self.assertTrue(device in devicetree.devices)
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+class NVMeFabricsNamespaceDevicePopulatorTestCase(PopulatorHelperTestCase):
Vojtech Trefny 65e9f9
+    helper_class = NVMeFabricsNamespaceDevicePopulator
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    @patch("os.path.join")
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_cdrom", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_dm", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_loop", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_md", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_partition", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_disk", return_value=True)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_nvme_namespace", return_value=True)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_nvme_fabrics", return_value=True)
Vojtech Trefny 65e9f9
+    def test_match(self, *args):
Vojtech Trefny 65e9f9
+        """Test matching of NVMe namespace device populator."""
Vojtech Trefny 65e9f9
+        device_is_nvme_fabrics = args[0]
Vojtech Trefny 65e9f9
+        self.assertTrue(self.helper_class.match(None))
Vojtech Trefny 65e9f9
+        device_is_nvme_fabrics.return_value = False
Vojtech Trefny 65e9f9
+        self.assertFalse(self.helper_class.match(None))
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    @patch("os.path.join")
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_cdrom", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_dm", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_loop", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_md", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_partition", return_value=False)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_disk", return_value=True)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_nvme_namespace", return_value=True)
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_is_nvme_fabrics", return_value=True)
Vojtech Trefny 65e9f9
+    def test_get_helper(self, *args):
Vojtech Trefny 65e9f9
+        """Test get_device_helper for NVMe namespaces."""
Vojtech Trefny 65e9f9
+        device_is_nvme_fabrics = args[0]
Vojtech Trefny 65e9f9
+        data = {}
Vojtech Trefny 65e9f9
+        self.assertEqual(get_device_helper(data), self.helper_class)
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        # verify that setting one of the required True return values to False prevents success
Vojtech Trefny 65e9f9
+        device_is_nvme_fabrics.return_value = False
Vojtech Trefny 65e9f9
+        self.assertNotEqual(get_device_helper(data), self.helper_class)
Vojtech Trefny 65e9f9
+        device_is_nvme_fabrics.return_value = True
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+    @patch("blivet.udev.device_get_name")
Vojtech Trefny 65e9f9
+    def test_run(self, *args):
Vojtech Trefny 65e9f9
+        """Test disk device populator."""
Vojtech Trefny 65e9f9
+        device_get_name = args[0]
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        devicetree = DeviceTree()
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        # set up some fake udev data to verify handling of specific entries
Vojtech Trefny 65e9f9
+        data = {'SYS_PATH': 'dummy', 'DEVNAME': 'dummy', 'ID_PATH': 'dummy'}
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        device_name = "nop"
Vojtech Trefny 65e9f9
+        device_get_name.return_value = device_name
Vojtech Trefny 65e9f9
+        helper = self.helper_class(devicetree, data)
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        device = helper.run()
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+        self.assertIsInstance(device, NVMeFabricsNamespaceDevice)
Vojtech Trefny 65e9f9
+        self.assertTrue(device.exists)
Vojtech Trefny 65e9f9
+        self.assertTrue(device.is_disk)
Vojtech Trefny 65e9f9
+        self.assertTrue(device in devicetree.devices)
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
+
Vojtech Trefny 65e9f9
 class MDDevicePopulatorTestCase(PopulatorHelperTestCase):
Vojtech Trefny 65e9f9
     helper_class = MDDevicePopulator
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
-- 
Vojtech Trefny 65e9f9
2.38.1
Vojtech Trefny 65e9f9
Vojtech Trefny 65e9f9
Vojtech Trefny 65e9f9
From af6ad7ff2f08180672690910d453158bcd463936 Mon Sep 17 00:00:00 2001
Vojtech Trefny 65e9f9
From: Vojtech Trefny <vtrefny@redhat.com>
Vojtech Trefny 65e9f9
Date: Fri, 2 Dec 2022 12:20:47 +0100
Vojtech Trefny 65e9f9
Subject: [PATCH 2/3] Add transport and address to NVMeController info
Vojtech Trefny 65e9f9
Vojtech Trefny 65e9f9
---
Vojtech Trefny 65e9f9
 blivet/devices/disk.py | 9 +++++++--
Vojtech Trefny 65e9f9
 1 file changed, 7 insertions(+), 2 deletions(-)
Vojtech Trefny 65e9f9
Vojtech Trefny 65e9f9
diff --git a/blivet/devices/disk.py b/blivet/devices/disk.py
Vojtech Trefny 65e9f9
index b5e25939..796b5b03 100644
Vojtech Trefny 65e9f9
--- a/blivet/devices/disk.py
Vojtech Trefny 65e9f9
+++ b/blivet/devices/disk.py
Vojtech Trefny 65e9f9
@@ -730,7 +730,8 @@ class NVDIMMNamespaceDevice(DiskDevice):
Vojtech Trefny 65e9f9
         return self._sector_size
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
-NVMeController = namedtuple("NVMeController", ["name", "serial", "nvme_ver", "id", "subsysnqn"])
Vojtech Trefny 65e9f9
+NVMeController = namedtuple("NVMeController", ["name", "serial", "nvme_ver", "id", "subsysnqn",
Vojtech Trefny 65e9f9
+                                               "transport", "transport_address"])
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
 class NVMeNamespaceDevice(DiskDevice):
Vojtech Trefny 65e9f9
@@ -792,11 +793,15 @@ class NVMeNamespaceDevice(DiskDevice):
Vojtech Trefny 65e9f9
             except GLib.GError as err:
Vojtech Trefny 65e9f9
                 log.debug("Failed to get controller info for %s: %s", cpath, str(err))
Vojtech Trefny 65e9f9
                 continue
Vojtech Trefny 65e9f9
+            ctrans = util.get_sysfs_attr(controller, "transport")
Vojtech Trefny 65e9f9
+            ctaddr = util.get_sysfs_attr(controller, "address")
Vojtech Trefny 65e9f9
             self._controllers.append(NVMeController(name=os.path.basename(cpath),
Vojtech Trefny 65e9f9
                                                     serial=cinfo.serial_number,
Vojtech Trefny 65e9f9
                                                     nvme_ver=cinfo.nvme_ver,
Vojtech Trefny 65e9f9
                                                     id=cinfo.ctrl_id,
Vojtech Trefny 65e9f9
-                                                    subsysnqn=cinfo.subsysnqn))
Vojtech Trefny 65e9f9
+                                                    subsysnqn=cinfo.subsysnqn,
Vojtech Trefny 65e9f9
+                                                    transport=ctrans,
Vojtech Trefny 65e9f9
+                                                    transport_address=ctaddr))
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
         return self._controllers
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
-- 
Vojtech Trefny 65e9f9
2.38.1
Vojtech Trefny 65e9f9
Vojtech Trefny 65e9f9
Vojtech Trefny 65e9f9
From a04538936ff62958c272b5e2b2657d177df1ef13 Mon Sep 17 00:00:00 2001
Vojtech Trefny 65e9f9
From: Vojtech Trefny <vtrefny@redhat.com>
Vojtech Trefny 65e9f9
Date: Thu, 8 Dec 2022 13:15:33 +0100
Vojtech Trefny 65e9f9
Subject: [PATCH 3/3] Add additional identifiers to NVMeNamespaceDevice
Vojtech Trefny 65e9f9
Vojtech Trefny 65e9f9
---
Vojtech Trefny 65e9f9
 blivet/devices/disk.py           | 2 ++
Vojtech Trefny 65e9f9
 blivet/populator/helpers/disk.py | 3 +++
Vojtech Trefny 65e9f9
 2 files changed, 5 insertions(+)
Vojtech Trefny 65e9f9
Vojtech Trefny 65e9f9
diff --git a/blivet/devices/disk.py b/blivet/devices/disk.py
Vojtech Trefny 65e9f9
index 796b5b03..8842b4dc 100644
Vojtech Trefny 65e9f9
--- a/blivet/devices/disk.py
Vojtech Trefny 65e9f9
+++ b/blivet/devices/disk.py
Vojtech Trefny 65e9f9
@@ -756,6 +756,8 @@ class NVMeNamespaceDevice(DiskDevice):
Vojtech Trefny 65e9f9
             :type nsid: int
Vojtech Trefny 65e9f9
         """
Vojtech Trefny 65e9f9
         self.nsid = kwargs.pop("nsid", 0)
Vojtech Trefny 65e9f9
+        self.eui64 = kwargs.pop("eui64", "")
Vojtech Trefny 65e9f9
+        self.nguid = kwargs.pop("nguid", "")
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
         DiskDevice.__init__(self, device, **kwargs)
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
diff --git a/blivet/populator/helpers/disk.py b/blivet/populator/helpers/disk.py
Vojtech Trefny 65e9f9
index 9ed1eebe..cf20d302 100644
Vojtech Trefny 65e9f9
--- a/blivet/populator/helpers/disk.py
Vojtech Trefny 65e9f9
+++ b/blivet/populator/helpers/disk.py
Vojtech Trefny 65e9f9
@@ -282,6 +282,9 @@ class NVMeNamespaceDevicePopulator(DiskDevicePopulator):
Vojtech Trefny 65e9f9
             log.debug("Failed to get namespace info for %s: %s", path, str(err))
Vojtech Trefny 65e9f9
         else:
Vojtech Trefny 65e9f9
             kwargs["nsid"] = ninfo.nsid
Vojtech Trefny 65e9f9
+            kwargs["uuid"] = ninfo.uuid
Vojtech Trefny 65e9f9
+            kwargs["eui64"] = ninfo.eui64
Vojtech Trefny 65e9f9
+            kwargs["nguid"] = ninfo.nguid
Vojtech Trefny 65e9f9
 
Vojtech Trefny 65e9f9
         log.info("%s is an NVMe local namespace device", udev.device_get_name(self.data))
Vojtech Trefny 65e9f9
         return kwargs
Vojtech Trefny 65e9f9
-- 
Vojtech Trefny 65e9f9
2.38.1
Vojtech Trefny 65e9f9