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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ using setup.py.

Common Package Dependencies and Problems
-----------------------------------------
Both python2 and python3 are supported via use of the 'python-six'
package.
Python3 is supported.

nvmetcli uses the 'pyparsing' package -- running nvmetcli without this
package may produce hard-to-decipher errors.
Expand Down
2 changes: 1 addition & 1 deletion nvmet/__init__.py
Original file line number Diff line number Diff line change
@@ -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
137 changes: 129 additions & 8 deletions nvmet/nvme.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
import stat
import uuid
import json
import subprocess
import shlex
from glob import iglob as glob
from six import iteritems, moves

DEFAULT_SAVE_FILE = '/etc/nvmet/config.json'

Expand Down Expand Up @@ -220,7 +221,7 @@ def dump(self):

def _setup_attrs(self, attr_dict, err_func):
for group in self.attr_groups:
for name, value in iteritems(attr_dict.get(group, {})):
for name, value in attr_dict.get(group, {}).items():
try:
self.set_attr(group, name, value)
except CFSError as e:
Expand All @@ -234,6 +235,7 @@ class Root(CFSNode):
def __init__(self):
super(Root, self).__init__()

self.attr_groups = ['discovery']
if not os.path.isdir(self.configfs_dir):
self._modprobe('nvmet')

Expand All @@ -256,9 +258,22 @@ def _modprobe(self, modname):
# Try the ctypes library included with the libkmod itself.
try:
import kmod
kmod.Kmod().modprobe(modname)
except Exception as e:
pass

try:
kmod.Kmod().modprobe(modname)
except Exception as e:
pass
except ImportError:
# Try the binary specified in /proc
try:
modprobe_cmd = None
with open('/proc/sys/kernel/modprobe', 'r') as f:
modprobe_cmd = f.read()
if modprobe_cmd:
subprocess.run(shlex.split(modprobe_cmd) + [modname],
check=False)
except Exception as e:
pass

def _list_subsystems(self):
self._check_self()
Expand Down Expand Up @@ -462,6 +477,13 @@ def _list_namespaces(self):
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)]
Expand All @@ -488,6 +510,9 @@ def remove_allowed_host(self, nqn):
except Exception as e:
raise CFSError("Could not unlink %s in configFS: %s" % (nqn, e))

def has_passthru(self):
return os.path.isdir(os.path.join(self.path, "passthru"))

@classmethod
def setup(cls, t, err_func):
'''
Expand All @@ -510,6 +535,8 @@ def setup(cls, t, err_func):
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)

Expand All @@ -518,6 +545,8 @@ def dump(self):
d['nqn'] = self.nqn
d['namespaces'] = [ns.dump() for ns in self.namespaces]
d['allowed_hosts'] = self.allowed_hosts
if self.has_passthru():
d['passthru'] = [self.passthru.dump()]
return d


Expand Down Expand Up @@ -556,7 +585,7 @@ def __init__(self, subsystem, nsid=None, mode='any'):
raise CFSError("Need NSID for lookup")

nsids = [n.nsid for n in subsystem.namespaces]
for index in moves.xrange(1, self.MAX_NSID + 1):
for index in range(1, self.MAX_NSID + 1):
if index not in nsids:
nsid = index
break
Expand All @@ -567,7 +596,7 @@ def __init__(self, subsystem, nsid=None, mode='any'):
if nsid < 1 or nsid > self.MAX_NSID:
raise CFSError("NSID must be 1 to %d" % self.MAX_NSID)

self.attr_groups = ['device', 'ana']
self.attr_groups = ['device', 'ana', 'resv']
self._subsystem = subsystem
self._nsid = nsid
self._path = "%s/namespaces/%d" % (self.subsystem.path, self.nsid)
Expand Down Expand Up @@ -629,6 +658,98 @@ def dump(self):
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 Passthru 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):
'''
Expand Down Expand Up @@ -816,7 +937,7 @@ def __init__(self, port, grpid, mode='any'):
raise CFSError("Need grpid for lookup")

grpids = [n.grpid for n in port.ana_groups]
for index in moves.xrange(2, self.MAX_GRPID + 1):
for index in range(2, self.MAX_GRPID + 1):
if index not in grpids:
grpid = index
break
Expand Down
Loading
Loading