[PATCH] nvmetcli: add support for NVMe passthru target
Nilay Shroff
nilay at linux.ibm.com
Wed Jun 11 03:39:29 PDT 2025
This change adds the support for configuring NVMe passthru target.
Signed-off-by: Nilay Shroff <nilay at linux.ibm.com>
---
nvmet/__init__.py | 2 +-
nvmet/nvme.py | 102 ++++++++++++++++++++++++++++++++++++++++++++++
nvmetcli | 77 ++++++++++++++++++++++++++++++++++
3 files changed, 180 insertions(+), 1 deletion(-)
diff --git a/nvmet/__init__.py b/nvmet/__init__.py
index cf172bd..5e2f525 100644
--- a/nvmet/__init__.py
+++ b/nvmet/__init__.py
@@ -1,2 +1,2 @@
-from .nvme import Root, Subsystem, Namespace, Port, Host, Referral, ANAGroup,\
+from .nvme import Root, Subsystem, Namespace, Port, Host, Referral, ANAGroup, Passthru, \
DEFAULT_SAVE_FILE
diff --git a/nvmet/nvme.py b/nvmet/nvme.py
index 59efdb5..d462ea2 100644
--- a/nvmet/nvme.py
+++ b/nvmet/nvme.py
@@ -462,6 +462,13 @@ class Subsystem(CFSNode):
namespaces = property(_list_namespaces,
doc="Get the list of Namespaces for the Subsystem.")
+ def _get_passthru(self):
+ self._check_self()
+ return Passthru(self)
+
+ passthru = property(_get_passthru,
+ doc="Get the passthru node for the subsystem")
+
def _list_allowed_hosts(self):
return [os.path.basename(name)
for name in os.listdir("%s/allowed_hosts/" % self._path)]
@@ -510,6 +517,8 @@ class Subsystem(CFSNode):
Namespace.setup(s, ns, err_func)
for h in t.get('allowed_hosts', []):
s.add_allowed_host(h)
+ for pt in t.get('passthru', []):
+ Passthru.setup(s, pt, err_func)
s._setup_attrs(t, err_func)
@@ -518,6 +527,7 @@ class Subsystem(CFSNode):
d['nqn'] = self.nqn
d['namespaces'] = [ns.dump() for ns in self.namespaces]
d['allowed_hosts'] = self.allowed_hosts
+ d['passthru'] = [self.passthru.dump()]
return d
@@ -629,6 +639,98 @@ class Namespace(CFSNode):
d['ana_grpid'] = self.grpid
return d
+class Passthru(CFSNode):
+ '''
+ This is an interface to a NVMe passthru in ConfigFS.
+ '''
+
+ def __init__(self, subsystem):
+ '''
+ @param subsystem: The parent Subsystem object.
+ @return: A Passthru object.
+ '''
+ super(Passthru, self).__init__()
+ self._path = "%s/passthru" % (subsystem.path)
+ self.attr_groups = ['device']
+
+ def _get_clear_ids(self):
+ self._check_self()
+ path = "%s/clear_ids" % self.path
+ _ids = 0
+ if os.path.isfile(path):
+ with open(path, 'r') as file_fd:
+ _ids = int(file_fd.read().strip())
+ return _ids
+
+ ids = property(_get_clear_ids,
+ doc = "Get the passthru namespace clear_ids attribute.")
+
+ def set_clear_ids(self, clear):
+ self._check_self()
+ path = "%s/clear_ids" % self.path
+ if os.path.isfile(path):
+ with open(path, 'w') as file_fd:
+ file_fd.write(str(clear))
+
+ def _get_admin_timeout(self):
+ self._check_self()
+ path = "%s/admin_timeout" % self.path
+ _timeout = 0
+ if os.path.isfile(path):
+ with open(path, 'r') as file_fd:
+ _timeout = int(file_fd.read().strip())
+ return _timeout
+
+ admin_timeout = property(_get_admin_timeout,
+ doc = "Get the passthru admin command timeout.")
+
+ def set_admin_timeout(self, timeout):
+ self._check_self()
+ path = "%s/admin_timeout" % self.path
+ if os.path.isfile(path):
+ with open(path, 'w') as file_fd:
+ file_fd.write(str(timeout))
+
+ def _get_io_timeout(self):
+ self._check_self()
+ path = "%s/io_timeout" % self.path
+ _timeout = 0
+ if os.path.isfile(path):
+ with open(path, 'r') as file_fd:
+ _timeout = int(file_fd.read().strip())
+ return _timeout
+
+ io_timeout = property(_get_io_timeout,
+ doc = "Get the passthru IO command timeout.")
+
+ def set_io_timeout(self, timeout):
+ self._check_self()
+ path = "%s/io_timeout" % self.path
+ if os.path.isfile(path):
+ with open(path, 'w') as file_fd:
+ file_fd.write(str(timeout))
+
+ @classmethod
+ def setup(cls, subsys, p, err_func):
+ try:
+ pt = Passthru(subsys)
+ except CFSError as e:
+ err_func("Could not create Namespace object: %s" % e)
+ return
+ pt._setup_attrs(p, err_func)
+ if 'clear_ids' in p:
+ pt.set_clear_ids(int(p['clear_ids']))
+ if 'admin_timeout' in p:
+ pt.set_admin_timeout(int(p['admin_timeout']))
+ if 'io_timeout' in p:
+ pt.set_io_timeout(int(p['io_timeout']))
+
+ def dump(self):
+ d = super(Passthru, self).dump()
+ d['clear_ids'] = self.ids
+ d['admin_timeout'] = self.admin_timeout
+ d['io_timeout'] = self.io_timeout
+ return d
class Port(CFSNode):
'''
diff --git a/nvmetcli b/nvmetcli
index d949891..810a7cd 100755
--- a/nvmetcli
+++ b/nvmetcli
@@ -166,6 +166,7 @@ class UISubsystemNode(UINode):
self._children = set([])
UINamespacesNode(self)
UIAllowedHostsNode(self)
+ UIPassthruNode(self)
def summary(self):
info = []
@@ -175,6 +176,82 @@ class UISubsystemNode(UINode):
info.append("serial=" + self.cfnode.get_attr("attr", "serial"))
return (", ".join(info), True)
+class UIPassthruNode(UINode):
+ ui_desc_device = {
+ 'path' : ('string', 'Passthru device path')
+ }
+
+ def __init__(self, parent):
+ passthru = nvme.Passthru(parent.cfnode)
+ UINode.__init__(self, 'passthru', parent, passthru)
+
+ def refresh(self):
+ pass
+
+ def ui_command_enable(self):
+ if self.cfnode.get_enable():
+ self.shell.log.info("The passthru is already enabled.")
+ else:
+ try:
+ self.cfnode.set_enable(1)
+ self.shell.log.info("The passthru has been enabled.")
+ except Exception as e:
+ raise configshell.ExecutionError(
+ "The passthru could not be enabled.")
+
+ def ui_command_disable(self):
+ if not self.cfnode.get_enable():
+ self.shell.log.info("The passthru is already disabled.")
+ else:
+ try:
+ self.cfnode.set_enable(0)
+ self.shell.log.info("The passthru has been disabled.")
+ except Exception as e:
+ raise configshell.ExecutionError(
+ "The passthru could not be disabled.")
+
+ def ui_command_clear_ids(self, clear):
+ '''
+ If I{clear} is set to non-zero then clears the passthru namespace
+ unique identifiers EUI/GUID/UUID.
+ '''
+ try:
+ self.cfnode.set_clear_ids(clear)
+ except Exception as e:
+ raise configshell.ExecutionError(
+ "Failed to set clear_ids for this passthru target.")
+
+ def ui_command_admin_timeout(self, timeout):
+ '''
+ Sets the timeout of admin passthru command.
+ '''
+ try:
+ self.cfnode.set_admin_timeout(timeout)
+ except Exception as e:
+ raise configshell.ExecutionError(
+ "Failed to set the admin passthru command timeout.")
+
+ def ui_command_io_timeout(self, timeout):
+ '''
+ Sets the timeout of IO passthru command.
+ '''
+ try:
+ self.cfnode.set_io_timeout(timeout)
+ except Exception as e:
+ raise configshell.ExecutionError(
+ "Failed to set the IO passthru command timeout.")
+
+ def summary(self):
+ info = []
+ info.append("path=" + self.cfnode.get_attr("device", "path"))
+ if self.cfnode.ids != 0:
+ info.append("clear_ids=" + str(self.cfnode.ids))
+ if self.cfnode.admin_timeout != 0:
+ info.append("admin_tomeout=" + str(self.cfnode.admin_timeout))
+ if self.cfnode.io_timeout != 0:
+ info.append("io_tomeout=" + str(self.cfnode.io_timeout))
+ info.append("enabled" if self.cfnode.get_enable() else "disabled")
+ return (", ".join(info), True)
class UINamespacesNode(UINode):
def __init__(self, parent):
--
2.49.0
More information about the Linux-nvme
mailing list