From: Martin Belanger Date: Thu, 13 Apr 2023 13:29:17 +0000 (-0400) Subject: Python: Add NBFT support X-Git-Tag: v1.5~43 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=eaf94893c22de00b471864e0f90a20e1b09899be;p=users%2Fsagi%2Flibnvme.git Python: Add NBFT support Signed-off-by: Martin Belanger --- diff --git a/libnvme/meson.build b/libnvme/meson.build index 742de11e..eaf51948 100644 --- a/libnvme/meson.build +++ b/libnvme/meson.build @@ -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 diff --git a/libnvme/nvme.i b/libnvme/nvme.i index cc939da9..0f2a7cdb 100644 --- a/libnvme/nvme.i +++ b/libnvme/nvme.i @@ -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; @@ -36,6 +37,10 @@ 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 index 00000000..2dea9363 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 index 00000000..b4dcdda7 --- /dev/null +++ b/libnvme/tests/test-nbft.py @@ -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()