All of lore.kernel.org
 help / color / mirror / Atom feed
From: hare@suse.de (Hannes Reinecke)
Subject: [PATCH] nvmetcli: ANA configuration support
Date: Fri, 27 Jul 2018 13:08:59 +0200	[thread overview]
Message-ID: <20180727110859.4212-1-hare@suse.de> (raw)

Add support for ANA configuration.

Signed-off-by: Hannes Reinecke <hare at suse.com>
---
 nvmet/__init__.py |  2 +-
 nvmet/nvme.py     | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 nvmetcli          | 61 +++++++++++++++++++++++++++++++++-
 3 files changed, 160 insertions(+), 2 deletions(-)

diff --git a/nvmet/__init__.py b/nvmet/__init__.py
index 6542d87..ca05de4 100644
--- a/nvmet/__init__.py
+++ b/nvmet/__init__.py
@@ -1 +1 @@
-from .nvme import Root, Subsystem, Namespace, Port, Host, Referral
+from .nvme import Root, Subsystem, Namespace, Port, Host, Referral, ANAGroup
diff --git a/nvmet/nvme.py b/nvmet/nvme.py
index 8253ea9..8909bd8 100644
--- a/nvmet/nvme.py
+++ b/nvmet/nvme.py
@@ -566,6 +566,24 @@ class Namespace(CFSNode):
     def _get_nsid(self):
         return self._nsid
 
+    def _get_grpid(self):
+        self._check_self()
+        _grpid = 0
+        path = "%s/ana_grpid" % self.path
+        if os.path.isfile(path):
+            with open(path, 'r') as file_fd:
+                _grpid = int(file_fd.read().strip())
+        return _grpid
+
+    def set_grpid(self, grpid):
+        self._check_self()
+        path = "%s/ana_grpid" % self.path
+        if os.path.isfile(path):
+            with open(path, 'w') as file_fd:
+                file_fd.write(str(grpid))
+
+    grpid = property(_get_grpid, doc="Get the ANA Group ID.")
+
     subsystem = property(_get_subsystem,
                          doc="Get the parent Subsystem object.")
     nsid = property(_get_nsid, doc="Get the NSID as an int.")
@@ -589,10 +607,12 @@ class Namespace(CFSNode):
             return
 
         ns._setup_attrs(n, err_func)
+        ns.set_grpid(int(n['ana_grpid']))
 
     def dump(self):
         d = super(Namespace, self).dump()
         d['nsid'] = self.nsid
+        d['ana_grpid'] = self.grpid
         return d
 
 
@@ -652,6 +672,8 @@ class Port(CFSNode):
         self._check_self()
         for s in self.subsystems:
             self.remove_subsystem(s)
+        for a in self.ana_groups:
+            a.delete()
         for r in self.referrals:
             r.delete()
         super(Port, self).delete()
@@ -664,6 +686,14 @@ class Port(CFSNode):
     referrals = property(_list_referrals,
                          doc="Get the list of Referrals for this Port.")
 
+    def _list_ana_groups(self):
+        self._check_self()
+        for d in os.listdir("%s/ana_groups/" % self._path):
+            yield ANAGroup(self, int(d), 'lookup')
+
+    ana_groups = property(_list_ana_groups,
+                          doc="Get the list of ANA Groups for this Port.")
+
     @classmethod
     def setup(cls, root, n, err_func):
         '''
@@ -685,6 +715,8 @@ class Port(CFSNode):
         port._setup_attrs(n, err_func)
         for s in n.get('subsystems', []):
             port.add_subsystem(s)
+        for a in n.get('ana_groups', []):
+            ANAGroup.setup(port, a, err_func)
         for r in n.get('referrals', []):
             Referral.setup(port, r, err_func)
 
@@ -692,6 +724,7 @@ class Port(CFSNode):
         d = super(Port, self).dump()
         d['portid'] = self.portid
         d['subsystems'] = self.subsystems
+        d['ana_groups'] = [a.dump() for a in self.ana_groups]
         d['referrals'] = [r.dump() for r in self.referrals]
         return d
 
@@ -747,6 +780,72 @@ class Referral(CFSNode):
         return d
 
 
+class ANAGroup(CFSNode):
+    '''
+    This is an interface to a NVMe ANA Group in configFS.
+    '''
+
+    MAX_GRPID = 128
+
+    def __repr__(self):
+        return "<ANA Group %d>" % self.grpid
+
+    def __init__(self, port, grpid, mode='any'):
+        super(ANAGroup, self).__init__()
+
+        if grpid is None:
+            if mode == 'lookup':
+                raise CFSError("Need grpid for lookup")
+
+            grpids = [n.grpid for n in port.ana_groups]
+            for index in xrange(2, self.MAX_GRPID + 1):
+                if index not in grpids:
+                    grpid = index
+                    break
+            if grpid is None:
+                raise CFSError("All ANA Group IDs 1-%d in use" % self.MAX_GRPID)
+        else:
+            grpid = int(grpid)
+            if grpid < 1 or grpid > self.MAX_GRPID:
+                raise CFSError("GRPID %d must be 1 to %d" % (grpid, self.MAX_GRPID))
+
+        self.attr_groups = ['ana']
+        self._port = port
+        self._grpid = grpid
+        self._path = "%s/ana_groups/%d" % (self._port.path, self.grpid)
+        self._create_in_cfs(mode)
+
+    def _get_grpid(self):
+        return self._grpid
+
+    grpid = property(_get_grpid, doc="Get the ANA Group ID.")
+
+    @classmethod
+    def setup(cls, port, n, err_func):
+        '''
+        Set up an ANA Group object based upon n dict, from saved config.
+        Guard against missing or bad dict items, but keep going.
+        Call 'err_func' for each error.
+        '''
+
+        if 'grpid' not in n:
+            err_func("'grpid' not defined for ANA Group")
+            return
+
+        try:
+            a = ANAGroup(port, n['grpid'])
+        except CFSError as e:
+            err_func("Could not create ANA Group object: %s" % e)
+            return
+
+        a._setup_attrs(n, err_func)
+
+    def dump(self):
+        d = super(ANAGroup, self).dump()
+        d['grpid'] = self.grpid
+        return d
+
+
 class Host(CFSNode):
     '''
     This is an interface to a NVMe Host in configFS.
diff --git a/nvmetcli b/nvmetcli
index 6b102a2..0193b66 100755
--- a/nvmetcli
+++ b/nvmetcli
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 
 '''
 Frontend to access to the NVMe target configfs hierarchy
@@ -258,6 +258,16 @@ class UINamespaceNode(UINode):
                 raise configshell.ExecutionError(
                     "The Namespace could not be disabled.")
 
+    def ui_command_grpid(self, grpid):
+        '''
+        Sets the ANA Group ID of the current Namespace to I{grpid}
+        '''
+        try:
+            self.cfnode.set_grpid(grpid)
+        except Exception as e:
+            raise configshell.ExecutionError(
+                "Failed to set ANA Group ID for this Namespace.")
+
     def summary(self):
         info = []
         info.append("path=" + self.cfnode.get_attr("device", "path"))
@@ -267,6 +277,8 @@ class UINamespaceNode(UINode):
         ns_nguid = self.cfnode.get_attr("device", "nguid")
         if ngiud_set(ns_nguid):
             info.append("nguid=" + ns_nguid)
+        if self.cfnode.grpid != 0:
+            info.append("grpid=" + str(self.cfnode.grpid))
         info.append("enabled" if self.cfnode.get_enable() else "disabled")
         ns_enabled = self.cfnode.get_enable()
         return (", ".join(info), True if ns_enabled == 1 else ns_enabled)
@@ -378,6 +390,7 @@ class UIPortNode(UINode):
     def __init__(self, parent, cfnode):
         UINode.__init__(self, str(cfnode.portid), parent, cfnode)
         UIPortSubsystemsNode(self)
+        UIANAGroupsNode(self)
         UIReferralsNode(self)
 
     def summary(self):
@@ -539,6 +552,52 @@ class UIReferralNode(UINode):
                     "The Referral could not be disabled.")
 
 
+class UIANAGroupsNode(UINode):
+    def __init__(self, parent):
+        UINode.__init__(self, 'ana_groups', parent)
+
+    def refresh(self):
+        self._children = set([])
+        for a in self.parent.cfnode.ana_groups:
+            UIANAGroupNode(self, a)
+
+    def ui_command_create(self, grpid):
+        '''
+        Creates a new ANA Group.
+
+        SEE ALSO
+        ========
+        B{delete}
+        '''
+        a = nvme.ANAGroup(self.parent.cfnode, grpid, mode='create')
+        UIANAGroupNode(self, a)
+
+    def ui_command_delete(self, grpid):
+        '''
+        Deletes the ANA Group with the specified I{name}.
+
+        SEE ALSO
+        ========
+        B{create}
+        '''
+        a = nvme.ANAGroup(self.parent.cfnode, grpid, mode='lookup')
+        a.delete()
+        self.refresh()
+
+
+class UIANAGroupNode(UINode):
+    ui_desc_ana = {
+        'state' : ('string', 'ANA state'),
+    }
+
+    def __init__(self, parent, cfnode):
+        UINode.__init__(self, str(cfnode.grpid), parent, cfnode)
+
+    def summary(self):
+        info = []
+        info.append("state=" + self.cfnode.get_attr("ana", "state"))
+        return (", ".join(info), True)
+
 class UIHostsNode(UINode):
     def __init__(self, parent):
         UINode.__init__(self, 'hosts', parent)
-- 
2.13.7

             reply	other threads:[~2018-07-27 11:08 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-27 11:08 Hannes Reinecke [this message]
2018-10-06 12:43 ` [PATCH] nvmetcli: ANA configuration support Christoph Hellwig
2018-10-06 13:09   ` Hannes Reinecke
2018-10-17  7:11     ` Christoph Hellwig
2018-11-08  8:48       ` Christoph Hellwig
2018-11-08  8:59         ` Hannes Reinecke
  -- strict thread matches above, loose matches on Subject: below --
2018-06-07  9:57 [PATCH nvmetcli] " Hannes Reinecke
2018-06-07 10:00 ` Johannes Thumshirn
2018-06-07 12:12   ` Christoph Hellwig
2018-06-07 12:38     ` Hannes Reinecke

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180727110859.4212-1-hare@suse.de \
    --to=hare@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.