From 61d0a8bdffbbb7229776d2f4f2e79ed22d21551f Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 1 Jul 2023 00:36:10 +0200 Subject: Merging upstream version 1.5. Signed-off-by: Daniel Baumann --- libnvme/meson.build | 20 +++- libnvme/nvme.i | 253 ++++++++++++++++++++++++++++++++++++++++++++- libnvme/tests/NBFT | Bin 0 -> 1017 bytes libnvme/tests/test-nbft.py | 93 +++++++++++++++++ 4 files changed, 357 insertions(+), 9 deletions(-) create mode 100644 libnvme/tests/NBFT create mode 100755 libnvme/tests/test-nbft.py (limited to 'libnvme') diff --git a/libnvme/meson.build b/libnvme/meson.build index 4138df3..12a601e 100644 --- a/libnvme/meson.build +++ b/libnvme/meson.build @@ -8,6 +8,7 @@ want_python = get_option('python') if want_python.disabled() build_python_bindings = false + py3_dep = dependency('', required: false) # Needed for muon else python3 = import('python').find_installation('python3') py3_dep = python3.dependency(required: want_python) @@ -17,11 +18,19 @@ else endif if build_python_bindings + r = run_command(swig, ['-version'], check: true) # Returns: "\nSWIG Version 4.1.1\n\nCompiled with ..." + swig_version = r.stdout().split('\n')[1].split()[2].strip() + if swig_version.version_compare('<4.1.0') + swig_cmd = [swig, '-python', '-py3', '-o', '@OUTPUT1@', '@INPUT0@'] + else + swig_cmd = [swig, '-python', '-o', '@OUTPUT1@', '@INPUT0@'] + endif + pymod_swig = custom_target( 'nvme.py', input: ['nvme.i'], output: ['nvme.py', 'nvme_wrap.c'], - command: [swig, '-python', '-py3', '-o', '@OUTPUT1@', '@INPUT0@'], + command: swig_cmd, install: true, install_dir: [python3.get_install_dir(pure: false, subdir: 'libnvme'), false], ) @@ -61,12 +70,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 cc939da..eb94cac 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; @@ -314,7 +315,7 @@ PyObject *hostid_from_file(); PyDict_SetItemStringDecRef(entry, "asqsz", val); val = PyLong_FromLong(e->eflags); PyDict_SetItemStringDecRef(entry, "eflags", val); - PyList_SetItem(obj, i, entry); /* steals ref. to entry */ + PyList_SetItem(obj, i, entry); /* steals ref. to object - no need to decref */ } $result = obj; }; @@ -325,7 +326,9 @@ PyObject *hostid_from_file(); struct nvme_root { %immutable config_file; + %immutable application; char *config_file; + char *application; }; struct nvme_host { @@ -345,10 +348,12 @@ struct nvme_subsystem { %immutable model; %immutable serial; %immutable firmware; + %immutable application; char *subsysnqn; char *model; char *serial; char *firmware; + char *application; }; struct nvme_ctrl { @@ -611,7 +616,9 @@ struct nvme_ns { %pythonappend nvme_ctrl::connect(struct nvme_host *h, struct nvme_fabrics_config *cfg) { - self.__parent = h # Keep a reference to parent to ensure garbage collection happens in the right order} + self.__host = h # Keep a reference to parent to ensure ctrl obj gets GCed before host} +%pythonappend nvme_ctrl::init(struct nvme_host *h, int instance) { + self.__host = h # Keep a reference to parent to ensure ctrl obj gets GCed before host} %extend nvme_ctrl { nvme_ctrl(struct nvme_root *r, const char *subsysnqn, @@ -733,7 +740,7 @@ struct nvme_ns { if (!obj) Py_RETURN_NONE; for (int i = 0; i < NVME_LOG_SUPPORTED_LOG_PAGES_MAX; i++) - PyList_SetItem(obj, i, PyLong_FromLong(le32_to_cpu(log.lid_support[i]))); /* steals ref. */ + PyList_SetItem(obj, i, PyLong_FromLong(le32_to_cpu(log.lid_support[i]))); /* steals ref. to object - no need to decref */ return obj; } @@ -812,12 +819,250 @@ 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); + + for (i = 0; i < ss->num_hfis; i++) + PyList_SetItem(hfis, i, PyLong_FromLong(ss->hfis[i]->index - 1)); /* steals ref. to object - no need to decref */ + + PyDict_SetItemStringDecRef(output, "hfi_indexes", 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", PyBool_FromLong(ss->pdu_header_digest_required)); + PyDict_SetItemStringDecRef(output, "data_digest_required", PyBool_FromLong(ss->data_digest_required)); + + return output; + } + + static PyObject *hfi_to_dict(struct nbft_info_hfi *hfi) + { + PyObject *output = PyDict_New(); + + 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", PyBool_FromLong(hfi->tcp_info.this_hfi_is_default_route)); + PyDict_SetItemStringDecRef(output, "dhcp_override", PyBool_FromLong(hfi->tcp_info.dhcp_override)); + } + + return output; + } + + static PyObject *discovery_to_dict(struct nbft_info_discovery *disc) + { + PyObject *output = PyDict_New(); + + if (disc->security) + PyDict_SetItemStringDecRef(output, "security_index", PyLong_FromLong(disc->security->index)); + if (disc->hfi) + PyDict_SetItemStringDecRef(output, "hfi_index", PyLong_FromLong(disc->hfi->index - 1)); + 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); + } + + { + size_t ss_num = 0; + struct nbft_info_subsystem_ns **ss; + PyObject *subsystem; + + /* First, let's find how many entries there are */ + for (ss = nbft->subsystem_ns_list; ss && *ss; ss++) + ss_num++; + + /* Now, let's fill the list using "(*ss)->index - 1" + as the index for writing to the list */ + subsystem = PyList_New(ss_num); + for (ss = nbft->subsystem_ns_list; ss && *ss; ss++) + PyList_SetItem(subsystem, (*ss)->index - 1, ssns_to_dict(*ss)); /* steals ref. to object - no need to decref */ + + PyDict_SetItemStringDecRef(output, "subsystem", subsystem); + } + + { + size_t hfi_num = 0; + struct nbft_info_hfi **hfi; + PyObject *hfis; + + /* First, let's find how many entries there are */ + for (hfi = nbft->hfi_list; hfi && *hfi; hfi++) + hfi_num++; + + /* Now, let's fill the list using "(*hfi)->index - 1" + as the index for writing to the list */ + hfis = PyList_New(hfi_num); + for (hfi = nbft->hfi_list; hfi && *hfi; hfi++) + PyList_SetItem(hfis, (*hfi)->index-1, hfi_to_dict(*hfi)); /* steals ref. to object - no need to decref */ + + PyDict_SetItemStringDecRef(output, "hfi", hfis); + } + + { + size_t disc_num = 0; + struct nbft_info_discovery **disc; + PyObject *discovery; + + /* First, let's find how many entries there are */ + for (disc = nbft->discovery_list; disc && *disc; disc++) + disc_num++; + + /* Now, let's fill the list using "(*disc)->index - 1" + as the index for writing to the list */ + discovery = PyList_New(disc_num); + for (disc = nbft->discovery_list; disc && *disc; disc++) + PyList_SetItem(discovery, (*disc)->index - 1, discovery_to_dict(*disc)); /* steals ref. to object - no need to decref */ + + PyDict_SetItemStringDecRef(output, "discovery", discovery); + } + + /* Security profiles are currently not implemented. */ + + 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. +#pragma SWIG nowarn=503 // Supress warnings about unnamed struct #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 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 index 0000000..3aeeba4 --- /dev/null +++ b/libnvme/tests/test-nbft.py @@ -0,0 +1,93 @@ +#!/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_index": 0, + "nqn": "nqn.2014-08.org.nvmexpress.discovery", + "uri": "nvme+tcp://100.71.103.50:8009/", + } + ], + "hfi": [ + { + "dhcp_override": True, + "dhcp_server_ipaddr": "100.71.245.254", + "gateway_ipaddr": "100.71.245.254", + "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": True, + "host_nqn_configured": True, + "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": False, + "hfi_indexes": [0], + "nid": "c82404ed9c15f53b8ccf0968002e0fca", + "nid_type": "nguid", + "nsid": 148, + "pdu_header_digest_required": False, + "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": False, + "hfi_indexes": [0], + "nid": "c82404ed9c15f53b8ccf0968002e0fca", + "nid_type": "nguid", + "nsid": 148, + "pdu_header_digest_required": False, + "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""" + actual_nbft = nvme.nbft_get(args.filename) + self.assertEqual(actual_nbft, 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() -- cgit v1.2.3