]> www.infradead.org Git - users/sagi/libnvme.git/commitdiff
Python: Add NBFT support
authorMartin Belanger <martin.belanger@dell.com>
Thu, 13 Apr 2023 13:29:17 +0000 (09:29 -0400)
committerDaniel Wagner <wagi@monom.org>
Mon, 17 Apr 2023 07:47:05 +0000 (09:47 +0200)
Signed-off-by: Martin Belanger <martin.belanger@dell.com>
libnvme/meson.build
libnvme/nvme.i
libnvme/tests/NBFT [new file with mode: 0644]
libnvme/tests/test-nbft.py [new file with mode: 0755]

index 742de11e3aa005e01c425c1c60ea2c49de6a2e2a..eaf5194820b6b0fb1b431d6ea9a25b6ab32ba693 100644 (file)
@@ -62,12 +62,13 @@ if build_python_bindings
     test('[Python] import libnvme', python3, args: ['-c', 'from libnvme import nvme'], env: test_env, depends: pynvme_clib)
 
     py_tests = [ 
-        [ 'create ctrl object', files('tests/create-ctrl-obj.py') ], 
-        [ 'SIGSEGV during gc', files('tests/gc.py') ],
+        [ 'create ctrl object', [ files('tests/create-ctrl-obj.py'), ] ],
+        [ 'SIGSEGV during gc', [ files('tests/gc.py'), ] ],
+        [ 'Read NBFT file', [ files('tests/test-nbft.py'), '--filename', join_paths(meson.current_source_dir(), 'tests', 'NBFT') ] ],
     ]
     foreach test: py_tests
         description = test[0]
-        py_script   = test[1]
-        test('[Python] ' + description, python3, args: [py_script, ], env: test_env, depends: pynvme_clib)
+        args = test[1]
+        test('[Python] ' + description, python3, args: args, env: test_env, depends: pynvme_clib)
     endforeach
 endif
index cc939da91a21d79edc1a1a683bc2922f138333b3..0f2a7cdbb6adb06fe6030837d4e08ae2dba0fbb0 100644 (file)
@@ -28,6 +28,7 @@
        #include "nvme/log.h"
        #include "nvme/ioctl.h"
        #include "nvme/types.h"
+       #include "nvme/nbft.h"
 
        static int host_iter_err = 0;
        static int subsys_iter_err = 0;
        static int connect_err = 0;
        static int discover_err = 0;
 
+       static void PyList_AppendDecRef(PyObject *p, PyObject *val) {
+           PyList_Append(p, val); /* Does NOT steal reference to val .. */
+           Py_XDECREF(val);       /* .. therefore decrement ref. count. */
+       }
        static void PyDict_SetItemStringDecRef(PyObject * p, const char *key, PyObject *val) {
                PyDict_SetItemString(p, key, val); /* Does NOT steal reference to val .. */
                Py_XDECREF(val);                   /* .. therefore decrement ref. count. */
@@ -812,12 +817,229 @@ struct nvme_ns {
        }
 %};
 
+/******
+  NBFT
+ ******/
+%{
+       static PyObject *ssns_to_dict(struct nbft_info_subsystem_ns *ss)
+       {
+               unsigned int i;
+               PyObject *output = PyDict_New();
+               PyObject *hfis = PyList_New(ss->num_hfis);
+
+               PyDict_SetItemStringDecRef(output, "index", PyLong_FromLong(ss->index));
+
+               PyDict_SetItemStringDecRef(output, "num_hfis", PyLong_FromLong(ss->num_hfis));
+               for (i = 0; i < ss->num_hfis; i++)
+                       PyList_SetItem(hfis, i, PyLong_FromLong(ss->hfis[i]->index)); /* steals ref. to object - no need to decref */
+
+               PyDict_SetItemStringDecRef(output, "hfis", hfis);
+
+               PyDict_SetItemStringDecRef(output, "trtype", PyUnicode_FromString(ss->transport));
+               PyDict_SetItemStringDecRef(output, "traddr", PyUnicode_FromString(ss->traddr));
+               PyDict_SetItemStringDecRef(output, "trsvcid", PyUnicode_FromString(ss->trsvcid));
+               PyDict_SetItemStringDecRef(output, "subsys_port_id", PyLong_FromLong(ss->subsys_port_id));
+               PyDict_SetItemStringDecRef(output, "nsid", PyLong_FromLong(ss->nsid));
+
+               {
+                       PyObject *nid;
+                       switch (ss->nid_type) {
+                       case NBFT_INFO_NID_TYPE_EUI64:
+                               PyDict_SetItemStringDecRef(output, "nid_type", PyUnicode_FromString("eui64"));
+                               nid = PyUnicode_FromFormat("%02x%02x%02x%02x%02x%02x%02x%02x",
+                                                          ss->nid[0], ss->nid[1], ss->nid[2], ss->nid[3],
+                                                          ss->nid[4], ss->nid[5], ss->nid[6], ss->nid[7]);
+                               break;
+
+                       case NBFT_INFO_NID_TYPE_NGUID:
+                               PyDict_SetItemStringDecRef(output, "nid_type", PyUnicode_FromString("nguid"));
+                               nid = PyUnicode_FromFormat("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+                                                          ss->nid[0], ss->nid[1], ss->nid[2], ss->nid[3],
+                                                          ss->nid[4], ss->nid[5], ss->nid[6], ss->nid[7],
+                                                          ss->nid[8], ss->nid[9], ss->nid[10], ss->nid[11],
+                                                          ss->nid[12], ss->nid[13], ss->nid[14], ss->nid[15]);
+                               break;
+
+                       case NBFT_INFO_NID_TYPE_NS_UUID:
+                       {
+                               char uuid_str[NVME_UUID_LEN_STRING];
+                               PyDict_SetItemStringDecRef(output, "nid_type", PyUnicode_FromString("uuid"));
+                               nvme_uuid_to_string(ss->nid, uuid_str);
+                               nid = PyUnicode_FromString(uuid_str);
+                               break;
+                       }
+
+                       default:
+                               nid = NULL;
+                               break;
+                       }
+                       if (nid)
+                               PyDict_SetItemStringDecRef(output, "nid", nid);
+               }
+
+               if (ss->subsys_nqn)
+                       PyDict_SetItemStringDecRef(output, "subsys_nqn", PyUnicode_FromString(ss->subsys_nqn));
+
+               PyDict_SetItemStringDecRef(output, "controller_id", PyLong_FromLong(ss->controller_id));
+               PyDict_SetItemStringDecRef(output, "asqsz", PyLong_FromLong(ss->asqsz));
+
+               if (ss->dhcp_root_path_string)
+                       PyDict_SetItemStringDecRef(output, "dhcp_root_path_string", PyUnicode_FromString(ss->dhcp_root_path_string));
+
+               PyDict_SetItemStringDecRef(output, "pdu_header_digest_required", PyLong_FromLong(ss->pdu_header_digest_required));
+               PyDict_SetItemStringDecRef(output, "data_digest_required", PyLong_FromLong(ss->data_digest_required));
+
+               return output;
+       }
+
+       static PyObject *hfi_to_dict(struct nbft_info_hfi *hfi)
+       {
+               PyObject *output = PyDict_New();
+
+               PyDict_SetItemStringDecRef(output, "index", PyLong_FromLong(hfi->index));
+               PyDict_SetItemStringDecRef(output, "trtype", PyUnicode_FromString(hfi->transport));
+
+               if (!strcmp(hfi->transport, "tcp")) {
+                       PyDict_SetItemStringDecRef(output, "pcidev",
+                                                  PyUnicode_FromFormat("%x:%x:%x.%x",
+                                                                       ((hfi->tcp_info.pci_sbdf & 0xffff0000) >> 16),
+                                                                       ((hfi->tcp_info.pci_sbdf & 0x0000ff00) >> 8),
+                                                                       ((hfi->tcp_info.pci_sbdf & 0x000000f8) >> 3),
+                                                                       ((hfi->tcp_info.pci_sbdf & 0x00000007) >> 0)));
+
+                       PyDict_SetItemStringDecRef(output, "mac_addr",
+                                                  PyUnicode_FromFormat("%02x:%02x:%02x:%02x:%02x:%02x",
+                                                                       hfi->tcp_info.mac_addr[0],
+                                                                       hfi->tcp_info.mac_addr[1],
+                                                                       hfi->tcp_info.mac_addr[2],
+                                                                       hfi->tcp_info.mac_addr[3],
+                                                                       hfi->tcp_info.mac_addr[4],
+                                                                       hfi->tcp_info.mac_addr[5]));
+
+                       PyDict_SetItemStringDecRef(output, "vlan", PyLong_FromLong(hfi->tcp_info.vlan));
+                       PyDict_SetItemStringDecRef(output, "ip_origin", PyLong_FromLong(hfi->tcp_info.ip_origin));
+                       PyDict_SetItemStringDecRef(output, "ipaddr", PyUnicode_FromString(hfi->tcp_info.ipaddr));
+                       PyDict_SetItemStringDecRef(output, "subnet_mask_prefix", PyLong_FromLong(hfi->tcp_info.subnet_mask_prefix));
+                       PyDict_SetItemStringDecRef(output, "gateway_ipaddr", PyUnicode_FromString(hfi->tcp_info.gateway_ipaddr));
+                       PyDict_SetItemStringDecRef(output, "route_metric", PyLong_FromLong(hfi->tcp_info.route_metric));
+                       PyDict_SetItemStringDecRef(output, "primary_dns_ipaddr", PyUnicode_FromString(hfi->tcp_info.primary_dns_ipaddr));
+                       PyDict_SetItemStringDecRef(output, "secondary_dns_ipaddr", PyUnicode_FromString(hfi->tcp_info.secondary_dns_ipaddr));
+                       PyDict_SetItemStringDecRef(output, "dhcp_server_ipaddr", PyUnicode_FromString(hfi->tcp_info.dhcp_server_ipaddr));
+
+                       if (hfi->tcp_info.host_name)
+                               PyDict_SetItemStringDecRef(output, "host_name", PyUnicode_FromString(hfi->tcp_info.host_name));
+
+                       PyDict_SetItemStringDecRef(output, "this_hfi_is_default_route", PyLong_FromLong(hfi->tcp_info.this_hfi_is_default_route));
+                       PyDict_SetItemStringDecRef(output, "dhcp_override", PyLong_FromLong(hfi->tcp_info.dhcp_override));
+               }
+
+               return output;
+       }
+
+       static PyObject *discovery_to_dict(struct nbft_info_discovery *disc)
+       {
+               PyObject *output = PyDict_New();
+
+               PyDict_SetItemStringDecRef(output, "index", PyLong_FromLong(disc->index));
+
+               if (disc->security)
+                       PyDict_SetItemStringDecRef(output, "security", PyLong_FromLong(disc->security->index));
+               if (disc->hfi)
+                       PyDict_SetItemStringDecRef(output, "hfi", PyLong_FromLong(disc->hfi->index));
+               if (disc->uri)
+                       PyDict_SetItemStringDecRef(output, "uri", PyUnicode_FromString(disc->uri));
+               if (disc->nqn)
+                       PyDict_SetItemStringDecRef(output, "nqn", PyUnicode_FromString(disc->nqn));
+
+               return output;
+       }
+
+       static PyObject *nbft_to_pydict(struct nbft_info *nbft)
+       {
+               PyObject *val;
+               PyObject *output = PyDict_New();
+
+               {
+                       PyObject *host = PyDict_New();
+
+                       if (nbft->host.nqn)
+                               PyDict_SetItemStringDecRef(host, "nqn", PyUnicode_FromString(nbft->host.nqn));
+                       if (nbft->host.id) {
+                               char uuid_str[NVME_UUID_LEN_STRING];
+                               nvme_uuid_to_string((unsigned char *)nbft->host.id, uuid_str);
+                               PyDict_SetItemStringDecRef(host, "id", PyUnicode_FromString(uuid_str));
+                       }
+
+                       PyDict_SetItemStringDecRef(host, "host_id_configured", PyBool_FromLong(nbft->host.host_id_configured));
+                       PyDict_SetItemStringDecRef(host, "host_nqn_configured", PyBool_FromLong(nbft->host.host_nqn_configured));
+
+                       val = PyUnicode_FromString(nbft->host.primary == NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_NOT_INDICATED ? "not indicated" :
+                                                  nbft->host.primary == NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_UNSELECTED ? "unselected" :
+                                                  nbft->host.primary == NBFT_INFO_PRIMARY_ADMIN_HOST_FLAG_SELECTED ? "selected" : "reserved");
+                       PyDict_SetItemStringDecRef(host, "primary_admin_host_flag", val);
+
+                       PyDict_SetItemStringDecRef(output, "host", host);
+               }
+
+               {
+                       struct nbft_info_subsystem_ns **ss;
+                       PyObject *subsystem = PyList_New(0);
+
+                       for (ss = nbft->subsystem_ns_list; ss && *ss; ss++)
+                               PyList_AppendDecRef(subsystem, ssns_to_dict(*ss));
+
+                       PyDict_SetItemStringDecRef(output, "subsystem", subsystem);
+               }
+
+               {
+                       struct nbft_info_hfi **hfi;
+                       PyObject *hfis = PyList_New(0);
+
+                       for (hfi = nbft->hfi_list; hfi && *hfi; hfi++)
+                               PyList_AppendDecRef(hfis, hfi_to_dict(*hfi));
+
+                       PyDict_SetItemStringDecRef(output, "hfi", hfis);
+               }
+
+               {
+                       struct nbft_info_discovery **disc;
+                       PyObject *discovery = PyList_New(0);
+
+                       for (disc = nbft->discovery_list; disc && *disc; disc++)
+                               PyList_AppendDecRef(discovery, discovery_to_dict(*disc));
+
+                       PyDict_SetItemStringDecRef(output, "discovery", discovery);
+               }
+
+               return output;
+       }
+
+       PyObject *nbft_get(const char * filename)
+       {
+               struct nbft_info *nbft;
+               PyObject *output;
+               int ret;
+
+               ret = nvme_nbft_read(&nbft, filename);
+               if (ret) {
+                       Py_RETURN_NONE;
+               }
+
+               output = nbft_to_pydict(nbft);
+               nvme_nbft_free(nbft);
+               return output;
+       }
+%};
+
+%feature("autodoc", "@return an NBFT table as a dict on success, None otherwise.\n"
+                   "@param filename: file to read") nbft_get;
+PyObject *nbft_get(const char * filename);
 
 // We want to swig all the #define and enum from types.h, but none of the structs.
 #define __attribute__(x)
 %rename($ignore, %$isclass) "";     // ignore all classes/structs
 %rename($ignore, %$isfunction) "";  // ignore all functions
 %rename($ignore, %$isunion) "";     // ignore all unions
-%rename($ignore, %$isvariable ) ""; // ignore all variables
+%rename($ignore, %$isvariable) "";  // ignore all variables
 
 %include "../src/nvme/types.h"
diff --git a/libnvme/tests/NBFT b/libnvme/tests/NBFT
new file mode 100644 (file)
index 0000000..2dea936
Binary files /dev/null and b/libnvme/tests/NBFT differ
diff --git a/libnvme/tests/test-nbft.py b/libnvme/tests/test-nbft.py
new file mode 100755 (executable)
index 0000000..b4dcdda
--- /dev/null
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+import os
+import unittest
+from libnvme import nvme
+from argparse import ArgumentParser
+
+
+class Testclass(unittest.TestCase):
+    def setUp(self):
+        self.expected_nbft = {
+            "discovery": [
+                {
+                    "hfi": 1,
+                    "index": 1,
+                    "nqn": "nqn.2014-08.org.nvmexpress.discovery",
+                    "uri": "nvme+tcp://100.71.103.50:8009/",
+                }
+            ],
+            "hfi": [
+                {
+                    "dhcp_override": 1,
+                    "dhcp_server_ipaddr": "100.71.245.254",
+                    "gateway_ipaddr": "100.71.245.254",
+                    "index": 1,
+                    "ip_origin": 82,
+                    "ipaddr": "100.71.245.232",
+                    "mac_addr": "b0:26:28:e8:7c:0e",
+                    "pcidev": "0:40:0.0",
+                    "primary_dns_ipaddr": "100.64.0.5",
+                    "route_metric": 500,
+                    "secondary_dns_ipaddr": "100.64.0.6",
+                    "subnet_mask_prefix": 24,
+                    "this_hfi_is_default_route": 1,
+                    "trtype": "tcp",
+                    "vlan": 0,
+                }
+            ],
+            "host": {
+                "host_id_configured": False,
+                "host_nqn_configured": False,
+                "id": "44454c4c-3400-1036-8038-b2c04f313233",
+                "nqn": "nqn.1988-11.com.dell:PowerEdge.R760.1234567",
+                "primary_admin_host_flag": "not indicated",
+            },
+            "subsystem": [
+                {
+                    "asqsz": 0,
+                    "controller_id": 5,
+                    "data_digest_required": 0,
+                    "hfis": [1],
+                    "index": 1,
+                    "nid": "c82404ed9c15f53b8ccf0968002e0fca",
+                    "nid_type": "nguid",
+                    "nsid": 148,
+                    "num_hfis": 1,
+                    "pdu_header_digest_required": 0,
+                    "subsys_nqn": "nqn.1988-11.com.dell:powerstore:00:2a64abf1c5b81F6C4549",
+                    "subsys_port_id": 0,
+                    "traddr": "100.71.103.48",
+                    "trsvcid": "4420",
+                    "trtype": "tcp",
+                },
+                {
+                    "asqsz": 0,
+                    "controller_id": 4166,
+                    "data_digest_required": 0,
+                    "hfis": [1],
+                    "index": 2,
+                    "nid": "c82404ed9c15f53b8ccf0968002e0fca",
+                    "nid_type": "nguid",
+                    "nsid": 148,
+                    "num_hfis": 1,
+                    "pdu_header_digest_required": 0,
+                    "subsys_nqn": "nqn.1988-11.com.dell:powerstore:00:2a64abf1c5b81F6C4549",
+                    "subsys_port_id": 0,
+                    "traddr": "100.71.103.49",
+                    "trsvcid": "4420",
+                    "trtype": "tcp",
+                },
+            ],
+        }
+
+    def test_read_nbft_file(self):
+        """Make sure we get expected data when reading from binary NBFT file"""
+        self.assertEqual(nvme.nbft_get(args.filename), self.expected_nbft)
+
+
+if __name__ == "__main__":
+    import sys
+
+    parser = ArgumentParser(description="Test NBFT")
+    parser.add_argument("--filename", default=None, help="NBFT binary file to read")
+    parser.add_argument("unittest_args", nargs="*")  # Grab everything else
+    args = parser.parse_args()
+    sys.argv[1:] = args.unittest_args
+
+    unittest.main()