From: Christoph Hellwig Date: Thu, 19 May 2016 14:14:36 +0000 (+0200) Subject: nvmet,nvmetcli: support per-port subsystem enablement X-Git-Tag: v0.2~13 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=3a12cbbc953cf1b1817c9260f7d8e5826b7b9242;p=users%2Fhch%2Fnvmetcli.git nvmet,nvmetcli: support per-port subsystem enablement Signed-off-by: Christoph Hellwig --- diff --git a/nvmet.json b/nvmet.json index 2064d4f..33c3545 100644 --- a/nvmet.json +++ b/nvmet.json @@ -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", @@ -30,6 +29,14 @@ }, "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" diff --git a/nvmet/nvme.py b/nvmet/nvme.py index d991d9b..e3b85a7 100644 --- a/nvmet/nvme.py +++ b/nvmet/nvme.py @@ -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 diff --git a/nvmet/test_nvmet.py b/nvmet/test_nvmet.py index d2dbb4a..38de70d 100644 --- a/nvmet/test_nvmet.py +++ b/nvmet/test_nvmet.py @@ -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) diff --git a/nvmetcli b/nvmetcli index c2ec343..35c5e67 100755 --- 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):