]> www.infradead.org Git - users/hch/nvmetcli.git/commitdiff
nvmet,nvmetcli: support per-port subsystem enablement
authorChristoph Hellwig <hch@lst.de>
Thu, 19 May 2016 14:14:36 +0000 (16:14 +0200)
committerChristoph Hellwig <hch@lst.de>
Mon, 23 May 2016 08:38:14 +0000 (10:38 +0200)
Signed-off-by: Christoph Hellwig <hch@lst.de>
nvmet.json
nvmet/nvme.py
nvmet/test_nvmet.py
nvmetcli

index 2064d4fc7c17e3d118cd0b9082a3f775e7f3aad9..33c35456cfde61bdc332118f262bd53d3298c3ac 100644 (file)
@@ -1,4 +1,5 @@
 {
+  "hosts": [], 
   "ports": [
     {
       "addr": {
@@ -8,21 +9,19 @@
         "trsvcid": "1023", 
         "trtype": "rdma"
       }, 
-      "enable": 1, 
-      "portid": 2
+      "portid": 2, 
+      "subsystems": [
+        "nqn.2014-08.org.nvmexpress:NVMf:uuid:77dca664-0d3e-4f67-b8b2-04c70e3f991d"
+      ]
     }
   ], 
   "subsystems": [
     {
+      "allowed_hosts": [], 
+      "attr": {
+        "allow_any_host": "1"
+      }, 
       "namespaces": [
-        {
-          "device": {
-            "nguid": "8a6a2e57-1a2d-2b41-8938-bc409c4ebb6c", 
-            "path": "/dev/ram0"
-          }, 
-          "enable": 0, 
-          "nsid": 2
-        }, 
         {
           "device": {
             "nguid": "289ae029-c396-3143-87e8-aec0d7a4b1a8", 
           }, 
           "enable": 1, 
           "nsid": 1
+        }, 
+        {
+          "device": {
+            "nguid": "8a6a2e57-1a2d-2b41-8938-bc409c4ebb6c", 
+            "path": "/dev/ram0"
+          }, 
+          "enable": 0, 
+          "nsid": 2
         }
       ], 
       "nqn": "nqn.2014-08.org.nvmexpress:NVMf:uuid:77dca664-0d3e-4f67-b8b2-04c70e3f991d"
index d991d9ba90b253fc20a5de8ce35a6bdd577bad69..e3b85a763ef963bcaf3f3bf4285883045207644f 100644 (file)
@@ -305,10 +305,10 @@ class Root(CFSNode):
         Remove entire current configuration.
         '''
 
-        for s in self.subsystems:
-            s.delete()
         for p in self.ports:
             p.delete()
+        for s in self.subsystems:
+            s.delete()
         for h in self.hosts:
             h.delete()
 
@@ -628,6 +628,41 @@ class Port(CFSNode):
     portid = property(_get_portid,
             doc="Get the Port ID as an int.")
 
+    def _list_subsystems(self):
+        return [os.path.basename(name)
+                for name in os.listdir("%s/subsystems/" % self._path)]
+
+    subsystems = property(_list_subsystems,
+                          doc="Get the list of Subsystem for this Port.")
+
+    def add_subsystem(self, nqn):
+        '''
+        Enable access to the Subsystem identified by I{nqn} through this Port.
+        '''
+        try:
+            os.symlink("%s/subsystems/%s" % (self.configfs_dir, nqn),
+                       "%s/subsystems/%s" % (self._path, nqn))
+        except Exception as e:
+            raise CFSError("Could not symlink %s in configFS: %s" % (nqn, e))
+
+    def remove_subsystem(self, nqn):
+        '''
+        Disable access to the Subsystem identified by I{nqn} through this Port.
+        '''
+        try:
+            os.unlink("%s/subsystems/%s" % (self._path, nqn))
+        except Exception as e:
+            raise CFSError("Could not unlink %s in configFS: %s" % (nqn, e))
+
+    def delete(self):
+        '''
+        Recursively deletes a Port object.
+        '''
+        self._check_self()
+        for s in self.subsystems:
+            self.remove_subsystem(s);
+        super(Port, self).delete()
+
     @classmethod
     def setup(cls, root, n, err_func):
         '''
@@ -647,10 +682,13 @@ class Port(CFSNode):
             return
 
         port._setup_attrs(n, err_func)
+        for s in n.get('subsystems', []):
+            port.add_subsystem(s)
 
     def dump(self):
         d = super(Port, self).dump()
         d['portid'] = self.portid
+        d['subsystems'] = self.subsystems
         return d
 
 
index d2dbb4a19aa46d492ac5ed0699eff62e3bf1f00b..38de70db9c94b7afc42c9986c9b0e7667a72c53a 100644 (file)
@@ -198,13 +198,16 @@ class TestNvmet(unittest.TestCase):
         root = nvme.Root()
         root.clear_existing()
 
+        s = nvme.Subsystem(nqn='testnqn', mode='create')
         p = nvme.Port(root, portid=0, mode='create')
 
-        self.assertFalse(p.get_enable())
+        # subsystem doesn't exists, should fail
+        self.assertRaises(nvme.CFSError, p.add_subsystem, 'invalidnqn')
+
         self.assertTrue('addr' in p.attr_groups)
 
         # no trtype set yet, should fail
-        self.assertRaises(nvme.CFSError, p.set_enable, 1)
+        self.assertRaises(nvme.CFSError, p.add_subsystem, 'testnqn')
 
         # now set trtype to loop and other attrs and enable
         p.set_attr('addr', 'trtype', 'loop')
@@ -212,11 +215,10 @@ class TestNvmet(unittest.TestCase):
         p.set_attr('addr', 'traddr', '192.168.0.1')
         p.set_attr('addr', 'treq', 'not required')
         p.set_attr('addr', 'trsvcid', '1023')
-        p.set_enable(1)
-        self.assertTrue(p.get_enable())
+        p.add_subsystem('testnqn')
 
-        # test double enable
-        p.set_enable(1)
+        # test double add
+        self.assertRaises(nvme.CFSError, p.add_subsystem, 'testnqn')
 
         # test that we can't write to attrs while enabled
         self.assertRaises(nvme.CFSError, p.set_attr, 'addr', 'trtype',
@@ -230,9 +232,9 @@ class TestNvmet(unittest.TestCase):
         self.assertRaises(nvme.CFSError, p.set_attr, 'addr', 'trsvcid',
                           '21')
 
-        # disable: once and twice
-        p.set_enable(0)
-        p.set_enable(0)
+        # remove: once and twice
+        p.remove_subsystem('testnqn')
+        self.assertRaises(nvme.CFSError, p.remove_subsystem, 'testnqn')
 
         # check that the attrs haven't been tampered with
         self.assertEqual(p.get_attr('addr', 'trtype'), 'loop')
@@ -241,8 +243,8 @@ class TestNvmet(unittest.TestCase):
         self.assertEqual(p.get_attr('addr', 'treq'), 'not required')
         self.assertEqual(p.get_attr('addr', 'trsvcid'), '1023')
 
-        # enable again, and remove while enabled
-        p.set_enable(1)
+        # add again, and try to remove while enabled
+        p.add_subsystem('testnqn')
         p.delete()
 
     def test_host(self):
@@ -352,7 +354,7 @@ class TestNvmet(unittest.TestCase):
         p.set_attr('addr', 'traddr', '192.168.0.1')
         p.set_attr('addr', 'treq', 'not required')
         p.set_attr('addr', 'trsvcid', '1023')
-        p.set_enable(1)
+        p.add_subsystem('testnqn')
 
         # save, clear, and restore
         root.save_to_file('test.json')
@@ -387,3 +389,5 @@ class TestNvmet(unittest.TestCase):
         self.assertEqual(p.get_attr('addr', 'traddr'), '192.168.0.1')
         self.assertEqual(p.get_attr('addr', 'treq'), 'not required')
         self.assertEqual(p.get_attr('addr', 'trsvcid'), '1023')
+        self.assertIn('testnqn', p.subsystems)
+        self.assertNotIn('testtnqn2', p.subsystems)
index c2ec343e8d75e7649420d37788b3d3f5a7c2320d..35c5e676e4e0c5e8f6358f2d5f8723320376be7c 100755 (executable)
--- a/nvmetcli
+++ b/nvmetcli
@@ -339,47 +339,73 @@ class UIPortsNode(UINode):
 class UIPortNode(UINode):
     def __init__(self, parent, cfnode):
         UINode.__init__(self, str(cfnode.portid), parent, cfnode)
+        UIPortSubsystemsNode(self)
 
     def status(self):
         if self.cfnode.get_enable():
             return "enabled"
         return "disabled"
 
-    def ui_command_enable(self):
+
+class UIPortSubsystemsNode(UINode):
+    def __init__(self, parent):
+        UINode.__init__(self, 'subsystems', parent)
+
+    def refresh(self):
+        self._children = set([])
+        for host in self.parent.cfnode.subsystems:
+            UIPortSubsystemNode(self, host)
+
+    def ui_command_create(self, nqn):
         '''
-        Enables the current Port.
+        Grants access to the subsystem specified by I{nqn} through the
+        parent port.
 
         SEE ALSO
         ========
-        B{disable}
+        B{delete}
         '''
-        if self.cfnode.get_enable():
-            self.shell.log.info("The Port is already enabled.")
+        self.parent.cfnode.add_subsystem(nqn)
+        UIPortSubsystemNode(self, nqn)
+
+    def ui_complete_create(self, parameters, text, current_param):
+        completions = []
+        if current_param == 'nqn':
+            for subsys in self.get_node('/subsystems').children:
+                completions.append(subsys.cfnode.nqn)
+
+        if len(completions) == 1:
+            return [completions[0] + ' ']
         else:
-            try:
-                self.cfnode.set_enable(1)
-                self.shell.log.info("The Port has been enabled.")
-            except Exception as e:
-                raise configshell.ExecutionError(
-                    "The Port could not be enabled.")
+            return completions
 
-    def ui_command_disable(self):
+    def ui_command_delete(self, nqn):
         '''
-        Disables the current Port.
+        Removes access to the subsystem specified by I{nqn} through the
+        parent port.
 
         SEE ALSO
         ========
-        B{enable}
+        B{create}
         '''
-        if not self.cfnode.get_enable():
-            self.shell.log.info("The Port is already disabled.")
+        self.parent.cfnode.remove_subsystem(nqn)
+        self.refresh()
+
+    def ui_complete_delete(self, parameters, text, current_param):
+        completions = []
+        if current_param == 'nqn':
+            for nqn in self.parent.cfnode.subsystems:
+                completions.append(nqn)
+
+        if len(completions) == 1:
+            return [completions[0] + ' ']
         else:
-            try:
-                self.cfnode.set_enable(0)
-                self.shell.log.info("The Port has been disabled.")
-            except Exception as e:
-                raise configshell.ExecutionError(
-                    "The Port could not be disabled.")
+            return completions
+
+
+class UIPortSubsystemNode(UINode):
+    def __init__(self, parent, nqn):
+        UINode.__init__(self, nqn, parent)
 
 
 class UIHostsNode(UINode):