Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package nvme-stas for openSUSE:Factory checked in at 2022-03-23 20:18:31 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/nvme-stas (Old) and /work/SRC/openSUSE:Factory/.nvme-stas.new.25692 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "nvme-stas" Wed Mar 23 20:18:31 2022 rev:2 rq:964187 version:1.0~rc4 Changes: -------- --- /work/SRC/openSUSE:Factory/nvme-stas/nvme-stas.changes 2022-03-04 00:18:46.180299506 +0100 +++ /work/SRC/openSUSE:Factory/.nvme-stas.new.25692/nvme-stas.changes 2022-03-23 20:20:24.506531671 +0100 @@ -1,0 +2,17 @@ +Wed Mar 23 08:23:34 UTC 2022 - Daniel Wagner <[email protected]> + +- Update to version 1.0-rc4: + * Check ignore-iface when creating TransportId object + * Adding man pages for ip-family and ignore-iface + * Add ignore-iface and ip-family conf. options + * Update the documentation + * Change default address returned by name resolver + * Filter out invalid IP addresses. (bsc#1197361) + * When reading the Host NQN, warn people if the NQN seems invalid + * Print descriptive message when unable to run stafctl/stacctl + * Use the newly added cntlrtype to check the type of controller + * minor fix to previous change to set the log level on libnvme + * LOG: enable libnvme debug when trace is enabled +- Added python3-netifaces dependency + +------------------------------------------------------------------- Old: ---- nvme-stas-1.0~rc2.obscpio New: ---- nvme-stas-1.0~rc4.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ nvme-stas.spec ++++++ --- /var/tmp/diff_new_pack.0VCu6Z/_old 2022-03-23 20:20:25.030531968 +0100 +++ /var/tmp/diff_new_pack.0VCu6Z/_new 2022-03-23 20:20:25.034531969 +0100 @@ -17,24 +17,27 @@ Name: nvme-stas -Version: 1.0~rc2 +Version: 1.0~rc4 Release: 0 Summary: NVMe STorage Appliance Services License: Apache-2.0 URL: https://github.com/linux-nvme/nvme-stas Source0: nvme-stas-%{version}.tar.gz BuildRequires: gobject-introspection +BuildRequires: libnvme-devel >= 1.0~7 BuildRequires: meson >= 0.52.0 BuildRequires: python3 BuildRequires: python3-dasbus BuildRequires: python3-gobject -BuildRequires: python3-libnvme >= 1.0~4 +BuildRequires: python3-libnvme >= 1.0~7 +BuildRequires: python3-netifaces BuildRequires: python3-pyudev BuildRequires: python3-systemd BuildRequires: systemd-rpm-macros Requires: python3-dasbus Requires: python3-gobject -Requires: python3-libnvme >= 1.0~4 +Requires: python3-libnvme >= 1.0~7 +Requires: python3-netifaces Requires: python3-pyudev Requires: python3-systemd ++++++ _service ++++++ --- /var/tmp/diff_new_pack.0VCu6Z/_old 2022-03-23 20:20:25.090532001 +0100 +++ /var/tmp/diff_new_pack.0VCu6Z/_new 2022-03-23 20:20:25.094532004 +0100 @@ -5,7 +5,7 @@ <param name="filename">nvme-stas</param> <!-- <param name="versionformat">@PARENT_TAG@+@TAG_OFFSET@</param> --> <param name="versionformat">@PARENT_TAG@</param> - <param name="revision">v1.0-rc2</param> + <param name="revision">v1.0-rc4</param> <param name="match-tag">v[01].[0-9]*</param> <param name="versionrewrite-pattern">v([^+]*)-rc([0-9]+)</param> <param name="versionrewrite-replacement">\1~rc\2</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.0VCu6Z/_old 2022-03-23 20:20:25.118532017 +0100 +++ /var/tmp/diff_new_pack.0VCu6Z/_new 2022-03-23 20:20:25.126532021 +0100 @@ -1,7 +1,7 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/linux-nvme/nvme-stas.git</param> - <param name="changesrevision">6cd94b82f7a96d4eac2a715a13782a605a38d502</param> + <param name="changesrevision">3bb3776d81e63eed5b254d453a0cd01fa27ef581</param> </service> </servicedata> (No newline at EOF) ++++++ nvme-stas-1.0~rc2.obscpio -> nvme-stas-1.0~rc4.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/.github/workflows/meson-test.yml new/nvme-stas-1.0~rc4/.github/workflows/meson-test.yml --- old/nvme-stas-1.0~rc2/.github/workflows/meson-test.yml 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/.github/workflows/meson-test.yml 2022-03-15 18:30:55.000000000 +0100 @@ -15,30 +15,14 @@ - uses: actions/checkout@v2 - name: Install requirements run: | - sudo apt-get install -y python3-pyudev python3-systemd python3-gi meson + sudo apt-get install -y libjson-c-dev python3-pyudev python3-systemd python3-gi python3-netifaces meson python -m pip install --upgrade pip dasbus pylint pyflakes - - name: Checkout libnvme - uses: actions/checkout@v2 + - uses: BSFishy/[email protected] with: - repository: linux-nvme/libnvme - path: libnvme - - - name: Build and install libnvme - run: | - cd libnvme - meson .build - cd .build - ninja - sudo meson install - - - name: Configure nvme-stas - run: PYTHONPATH=libnvme/.build meson .build - - - name: Test - run: | - cd .build - PYTHONPATH=../libnvme/.build meson test -v + setup-options: --werror + options: --verbose + action: test - name: Upload logs if: failure() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/.gitignore new/nvme-stas-1.0~rc4/.gitignore --- old/nvme-stas-1.0~rc2/.gitignore 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/.gitignore 2022-03-15 18:30:55.000000000 +0100 @@ -1,3 +1,6 @@ .build obj-x86_64-linux-gnu __pycache__ + +subprojects/* +!subprojects/*.wrap diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/DISTROS.md new/nvme-stas-1.0~rc4/DISTROS.md --- old/nvme-stas-1.0~rc2/DISTROS.md 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/DISTROS.md 2022-03-15 18:30:55.000000000 +0100 @@ -14,21 +14,33 @@ ## Run-time dependencies -nvme-stas requires Linux kernel version of 5.14 or later. This version of Linux introduces a new configuration parameter (`host-iface`) needed by the nvme-tcp kernel module. +nvme-stas is built on top of libnvme, which is used to interact with the kernel's NVMe driver (i.e. `drivers/nvme/host/`). To support all the features of nvme-stas, several changes to the Linux kernel are required. nvme-stas can also operate with older kernels, but with limited functionality. Kernel 5.18 provides all the features needed by nvme-stas. nvme-stas can also work with older kernels that include back-ported changes to the NVMe driver. -nvme-stas also depends on the following run-time libraries and modules. Note that versions listed are the versions that were tested with. With the exception of the Linux kernel 5.14, which is the mandatory minimum kernel required, all other libraries could potentially work with an earlier version. +The next table shows different features that were added to the NVMe driver and in which version of the Linux kernel they were added (the list of git patches can be found in addendum). Note that the ability to query the NVMe driver to determine what options it supports was added in 5.17. This is needed if nvme-stas is to make the right decision on whether a feature is supported. Otherwise, nvme-stas can only rely on the kernel version to decide what is supported. This can greatly limit the features supported on back-ported kernels. + +| Feature | Introduced in kernel version | +| ------------------------------------------------------------ | ---------------------------- | +| **`host-iface` option** - Ability to force TCP connections over a specific interface. Needed for zeroconf provisioning. | 5.14 | +| **TP8013 Support** - Discovery Controller (DC) Unique NQN. Allow the creation of connections to DC with a NQN other than the default `nqn.2014-08.org.nvmexpress.discovery` | 5.16 | +| **Query supported options** - Allow user-space applications to query which options the NVMe driver supports | 5.17 | +| **TP8010 Support** - Ability for a Host to register with a Discovery Controller. This version of the kernel introduces a new event to indicate to user-space apps (e.g. nvme-stas) when a connection to a DC is restored. This is used to trigger a re-registration of the host. This kernel also exposes the DC Type (dctype) attribute through the sysfs, which is needed to determine whether registration is supported. | 5.18 | + +nvme-stas also depends on the following run-time libraries and modules. Note that versions listed are the versions that were tested with. | Library | Min version | stafd | stacd | | ----------------------------------------------- | ----------- | ------------- | ------------- | | libnvme | 1.0 | **Mandatory** | **Mandatory** | | python3-dasbus | 1.6 | **Mandatory** | **Mandatory** | +| python3-netifaces | 0.10.4 | **Mandatory** | **Mandatory** | | python3-pyudev | 0.22.0 | **Mandatory** | **Mandatory** | | python3-systemd | 234 | **Mandatory** | **Mandatory** | -| python3-gi (Debian) OR python3-gobject (Fedora) | 3.40.1 | **Mandatory** | **Mandatory** | -| nvme-tcp (kernel module) | 5.14 | **Mandatory** | **Mandatory** | -| dbus-daemon | 1.12.20 | **Mandatory** | **Mandatory** | +| python3-gi (Debian) OR python3-gobject (Fedora) | 3.36.0 | **Mandatory** | **Mandatory** | +| nvme-tcp (kernel module) | 5.18 * | **Mandatory** | **Mandatory** | +| dbus-daemon | 1.12.2 | **Mandatory** | **Mandatory** | | avahi-daemon | 0.8 | **Mandatory** | Not required | +* Kernel 5.18 provides full functionality. nvme-stas can work with older kernels, but with limited functionality. + ## Things to do post installation ### D-Bus configuration @@ -68,3 +80,228 @@ ### Enabling and starting the daemons Lastly, the two daemons, `stafd` and `stacd`, should be enabled (e.g. `systemctl enable stafd.service stacd.service`) and started (e.g. `systemctl start stafd.service stacd.service`). + +# Addendum + +## Kernel patches + +Here's the list of kernel patches (added in kernels 5.14 to 5.18) that will enable all features of nvme-stas. + +``` +commit e3448b134426741902b6e2c07cbaf5f66cfd2ebc +Author: Martin Belanger <[email protected]> +Date: Tue Feb 8 14:18:02 2022 -0500 + + nvme: Expose cntrltype and dctype through sysfs + + TP8010 introduces the Discovery Controller Type attribute (dctype). + The dctype is returned in the response to the Identify command. This + patch exposes the dctype through the sysfs. Since the dctype depends on + the Controller Type (cntrltype), another attribute of the Identify + response, the patch also exposes the cntrltype as well. The dctype will + only be displayed for discovery controllers. + + A note about the naming of this attribute: + Although TP8010 calls this attribute the Discovery Controller Type, + note that the dctype is now part of the response to the Identify + command for all controller types. I/O, Discovery, and Admin controllers + all share the same Identify response PDU structure. Non-discovery + controllers as well as pre-TP8010 discovery controllers will continue + to set this field to 0 (which has always been the default for reserved + bytes). Per TP8010, the value 0 now means "Discovery controller type is + not reported" instead of "Reserved". One could argue that this + definition is correct even for non-discovery controllers, and by + extension, exposing it in the sysfs for non-discovery controllers is + appropriate. + + Signed-off-by: Martin Belanger <[email protected]> + +commit 68c483a105ce7107f1cf8e1ed6c2c2abb5baa551 +Author: Martin Belanger <[email protected]> +Date: Thu Feb 3 16:04:29 2022 -0500 + + nvme: send uevent on connection up + + When connectivity with a controller is lost, the driver will keep + trying to reconnect once every 10 sec. When connection is restored, + user-space apps need to be informed so that they can take proper + action. For example, TP8010 introduces the DIM PDU, which is used to + register with a discovery controller (DC). The DIM PDU is sent from + user-space. The DIM PDU must be sent every time a connection is + established with a DC. Therefore, the kernel must tell user-space apps + when connection is restored so that registration can happen. + + The uevent sent is a "change" uevent with environmental data + set to: "NVME_EVENT=connected". + + Signed-off-by: Martin Belanger <[email protected]> + Reviewed-by: Hannes Reinecke <[email protected]> + Reviewed-by: Sagi Grimberg <[email protected]> + Reviewed-by: Chaitanya Kulkarni <[email protected]> + +commit f18ee3d988157ebcadc9b7e5fd34811938f50223 +Author: Hannes Reinecke <[email protected]> +Date: Tue Dec 7 14:55:49 2021 +0100 + + nvme-fabrics: print out valid arguments when reading from /dev/nvme-fabrics + + Currently applications have a hard time figuring out which + nvme-over-fabrics arguments are supported for any given kernel; + the ioctl will return an error code on failure, and the application + has to guess whether this was due to an invalid argument or due + to a connection or controller error. + With this patch applications can read a list of supported + arguments by simply reading from /dev/nvme-fabrics, allowing + them to validate the connection string. + + Signed-off-by: Hannes Reinecke <[email protected]> + Reviewed-by: Chaitanya Kulkarni <[email protected]> + Signed-off-by: Christoph Hellwig <[email protected]> + + +commit e5ea42faa773c6a6bb5d9e9f5c2cc808940b5a55 +Author: Hannes Reinecke <[email protected]> +Date: Wed Sep 22 08:35:25 2021 +0200 + + nvme: display correct subsystem NQN + + With discovery controllers supporting unique subsystem NQNs the + actual subsystem NQN might be different from that one passed in + via the connect args. So add a helper to display the resulting + subsystem NQN. + + Signed-off-by: Hannes Reinecke <[email protected]> + Reviewed-by: Chaitanya Kulkarni <[email protected]> + Signed-off-by: Christoph Hellwig <[email protected]> + +commit 20e8b689c9088027b7495ffd6f80812c11ecc872 +Author: Hannes Reinecke <[email protected]> +Date: Wed Sep 22 08:35:24 2021 +0200 + + nvme: Add connect option 'discovery' + + Add a connect option 'discovery' to specify that the connection + should be made to a discovery controller, not a normal I/O controller. + With discovery controllers supporting unique subsystem NQNs we + cannot easily distinguish by the subsystem NQN if this should be + a discovery connection, but we need this information to blank out + options not supported by discovery controllers. + + Signed-off-by: Hannes Reinecke <[email protected]> + Reviewed-by: Chaitanya Kulkarni <[email protected]> + Signed-off-by: Christoph Hellwig <[email protected]> + +commit 954ae16681f6bdf684f016ca626329302a38e177 +Author: Hannes Reinecke <[email protected]> +Date: Wed Sep 22 08:35:23 2021 +0200 + + nvme: expose subsystem type in sysfs attribute 'subsystype' + + With unique discovery controller NQNs we cannot distinguish the + subsystem type by the NQN alone, but need to check the subsystem + type, too. + So expose the subsystem type in a new sysfs attribute 'subsystype'. + + Signed-off-by: Hannes Reinecke <[email protected]> + Reviewed-by: Chaitanya Kulkarni <[email protected]> + Signed-off-by: Christoph Hellwig <[email protected]> + + +commit 3ede8f72a9a2825efca23a3552e80a1202ea88fd +Author: Martin Belanger <[email protected]> +Date: Thu May 20 15:09:34 2021 -0400 + + nvme-tcp: allow selecting the network interface for connections + + In our application, we need a way to force TCP connections to go out a + specific IP interface instead of letting Linux select the interface + based on the routing tables. + + Add the 'host-iface' option to allow specifying the interface to use. + When the option host-iface is specified, the driver uses the specified + interface to set the option SO_BINDTODEVICE on the TCP socket before + connecting. + + This new option is needed in addtion to the existing host-traddr for + the following reasons: + + Specifying an IP interface by its associated IP address is less + intuitive than specifying the actual interface name and, in some cases, + simply doesn't work. That's because the association between interfaces + and IP addresses is not predictable. IP addresses can be changed or can + change by themselves over time (e.g. DHCP). Interface names are + predictable [1] and will persist over time. Consider the following + configuration. + + 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state ... + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 100.0.0.100/24 scope global lo + valid_lft forever preferred_lft forever + 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc ... + link/ether 08:00:27:21:65:ec brd ff:ff:ff:ff:ff:ff + inet 100.0.0.100/24 scope global enp0s3 + valid_lft forever preferred_lft forever + 3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc ... + link/ether 08:00:27:4f:95:5c brd ff:ff:ff:ff:ff:ff + inet 100.0.0.100/24 scope global enp0s8 + valid_lft forever preferred_lft forever + + The above is a VM that I configured with the same IP address + (100.0.0.100) on all interfaces. Doing a reverse lookup to identify the + unique interface associated with 100.0.0.100 does not work here. And + this is why the option host_iface is required. I understand that the + above config does not represent a standard host system, but I'm using + this to prove a point: "We can never know how users will configure + their systems". By te way, The above configuration is perfectly fine + by Linux. + + The current TCP implementation for host_traddr performs a + bind()-before-connect(). This is a common construct to set the source + IP address on a TCP socket before connecting. This has no effect on how + Linux selects the interface for the connection. That's because Linux + uses the Weak End System model as described in RFC1122 [2]. On the other + hand, setting the Source IP Address has benefits and should be supported + by linux-nvme. In fact, setting the Source IP Address is a mandatory + FedGov requirement (e.g. connection to a RADIUS/TACACS+ server). + Consider the following configuration. + + $ ip addr list dev enp0s8 + 3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc ... + link/ether 08:00:27:4f:95:5c brd ff:ff:ff:ff:ff:ff + inet 192.168.56.101/24 brd 192.168.56.255 scope global enp0s8 + valid_lft 426sec preferred_lft 426sec + inet 192.168.56.102/24 scope global secondary enp0s8 + valid_lft forever preferred_lft forever + inet 192.168.56.103/24 scope global secondary enp0s8 + valid_lft forever preferred_lft forever + inet 192.168.56.104/24 scope global secondary enp0s8 + valid_lft forever preferred_lft forever + + Here we can see that several addresses are associated with interface + enp0s8. By default, Linux always selects the default IP address, + 192.168.56.101, as the source address when connecting over interface + enp0s8. Some users, however, want the ability to specify a different + source address (e.g., 192.168.56.102, 192.168.56.103, ...). The option + host_traddr can be used as-is to perform this function. + + In conclusion, I believe that we need 2 options for TCP connections. + One that can be used to specify an interface (host-iface). And one that + can be used to set the source address (host-traddr). Users should be + allowed to use one or the other, or both, or none. Of course, the + documentation for host_traddr will need some clarification. It should + state that when used for TCP connection, this option only sets the + source address. And the documentation for host_iface should say that + this option is only available for TCP connections. + + References: + [1] https://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/ + [2] https://tools.ietf.org/html/rfc1122 + + Tested both IPv4 and IPv6 connections. + + Signed-off-by: Martin Belanger <[email protected]> + Reviewed-by: Sagi Grimberg <[email protected]> + Reviewed-by: Hannes Reinecke <[email protected]> + Signed-off-by: Christoph Hellwig <[email protected]> +``` + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/Dockerfile new/nvme-stas-1.0~rc4/Dockerfile --- old/nvme-stas-1.0~rc2/Dockerfile 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/Dockerfile 2022-03-15 18:30:55.000000000 +0100 @@ -1,20 +1,3 @@ -########################################################################################## -ARG registry=library -ARG base=fedora -ARG version=33 -FROM $registry/$base:$version as libnvme-builder - -WORKDIR /root - -# TODO: once libnvme project has a package or docker image, remove this stage and install libnvme -RUN dnf install -y git gcc g++ cmake libuuid-devel json-c-devel swig python-devel meson -ARG GITHUB_ORG=linux-nvme -ARG GITHUB_REPO=libnvme -ARG GITHUB_TOKEN -RUN git clone https://${GITHUB_TOKEN}github.com/${GITHUB_ORG}/${GITHUB_REPO} && \ - cd ${GITHUB_REPO} && meson .build && ninja -C .build && cd .build && DESTDIR=/root/install meson install - -########################################################################################## ARG registry=library ARG base=fedora ARG version=33 @@ -22,9 +5,10 @@ WORKDIR /root -RUN dnf install -y python3-dasbus python3-pyudev python3-systemd python3-gobject meson -# TODO: once libnvme project has a package, use: dnf install -y libnvme -COPY --from=libnvme-builder /root/install / +# for nvme-stas +RUN dnf install -y python3-dasbus python3-pyudev python3-systemd python3-gobject python3-netifaces meson +# for libnvme +RUN dnf install -y git gcc g++ cmake openssl-devel libuuid-devel json-c-devel swig python-devel meson COPY . . RUN meson .build && ninja -C .build && cd .build && meson install Binary files old/nvme-stas-1.0~rc2/Documentation/images/STAF-STAC-libnvme.png and new/nvme-stas-1.0~rc4/Documentation/images/STAF-STAC-libnvme.png differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/Makefile new/nvme-stas-1.0~rc4/Makefile --- old/nvme-stas-1.0~rc2/Makefile 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/Makefile 2022-03-15 18:30:55.000000000 +0100 @@ -12,7 +12,7 @@ RPM-PKG-DIR := ${BUILD-DIR}/rpm-pkg ${BUILD-DIR}: - ./configure + BUILD_DIR=${BUILD-DIR} ./configure @echo "Configuration located in: $@" @echo "-------------------------------------------------------" @@ -32,7 +32,7 @@ rm -rf ${BUILD-DIR} endif -.PHONY: install +.PHONY: install test install test: ${BUILD-DIR} cd ${BUILD-DIR} && meson $@ @@ -65,4 +65,4 @@ .PHONY: rpm rpm: dist - rpmbuild ${BUILD-DIR}/nvme-stas.spec + rpmbuild -ba ${BUILD-DIR}/nvme-stas.spec diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/README.md new/nvme-stas-1.0~rc4/README.md --- old/nvme-stas-1.0~rc2/README.md 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/README.md 2022-03-15 18:30:55.000000000 +0100 @@ -84,18 +84,16 @@ **Debian packages (tested on Ubuntu 20.04):** ```bash -sudo apt-get install -y python3-pyudev python3-systemd python3-gi +sudo apt-get install -y python3-pyudev python3-systemd python3-gi python3-netifaces sudo pip3 install dasbus ``` -**Yum packages (tested on Fedora 34):** +**RPM packages (tested on Fedora 34..35 and SLES15):** ```bash -sudo dnf install -y python3-dasbus python3-pyudev python3-systemd python3-gobject +sudo dnf install -y python3-dasbus python3-pyudev python3-systemd python3-gobject python3-netifaces ``` -Additionally, the `libnvme` library (with Python bindings) needs to be installed. Currently only available from source at: https://github.com/linux-nvme/libnvme - # STAF - STorage Appliance Finder @@ -139,7 +137,7 @@ # Build, install, unit tests -STAS uses the `meson` build system. Since STAS is a Python project, there is no code to build. However, the code needs to be installed using `meson`. Once installed, unit tests can be run with `meson` as well. +STAS uses the `meson` build system. Since STAS is a Python project, there is no code to build. However, the code needs to be installed using `meson`. Unit tests can also be run with `meson`. ## Using meson @@ -149,16 +147,23 @@ meson .build ``` -Note that the first line, `meson .build`, need only be called once. This analyzes the project and the host computer to determine if all the necessary tools are available. The result is in the directory named `.build`. +The command `meson .build` need only be called once. This analyzes the project and the host computer to determine if all the necessary tools and dependencies are available. The result is saved to the directory named `.build`. + +To compile the code: + +```bash +cd .build +ninja +``` -Do as follows to install the code: +To install the code: ```bash cd .build meson install ``` -Do as follows to run the unit tests: +To run the unit tests: ```bash cd .build @@ -174,7 +179,7 @@ make ``` -This performs the same operations as the meson approach described above. The `configure` script simply invokes `meson .build` and generates a `Makefile`. The generated `Makefile` provides the following operations. +This performs the same operations as the meson approach described above. The `configure` script simply invokes `meson .build`. | make command | Corresponding commands using meson | | ------------------ | ------------------------------------------------------------ | @@ -214,7 +219,7 @@ ## Generating man and html pages -nvme-stas uses the following programs to generate the documentation. These can be installed as shown in the "dependencies" section. +nvme-stas uses the following programs to generate the documentation. These can be installed as shown in the "dependencies" section below. - `xsltproc` - Used to convert DocBook XML notation to "man pages" and "html pages". - `gdbus-codegen` - Used to convert D-Bus IDL to DocBook XML notation. @@ -229,13 +234,13 @@ sudo apt-get install -y docbook-xml docbook-xsl xsltproc libglib2.0-dev ``` -**Yum packages (tested on Fedora 34):** +**RPM packages (tested on Fedora 34..35 and SLES15):** ```bash sudo dnf install -y docbook-style-xsl libxslt glib2-devel ``` -### Configuring to build the man and html pages +### Configuring and building the man and html pages By default, the documentation is not built. You need to run the `configure` as follows to tell meson that you want to build the documentation. You may need to first purge any previous configuration. @@ -245,7 +250,7 @@ make ``` - - - - +## Generating RPM and/or DEB packages +```bash +make rpm deb +``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/configure new/nvme-stas-1.0~rc4/configure --- old/nvme-stas-1.0~rc2/configure 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/configure 2022-03-15 18:30:55.000000000 +0100 @@ -7,7 +7,7 @@ # # Authors: Martin Belanger <[email protected]> # -BUILD_DIR=".build" +BUILD_DIR="${BUILD_DIR:-.build}" if [ ! -d ${BUILD_DIR} ]; then exec meson ${BUILD_DIR} "$@" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/debian/control new/nvme-stas-1.0~rc4/debian/control --- old/nvme-stas-1.0~rc2/debian/control 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/debian/control 2022-03-15 18:30:55.000000000 +0100 @@ -11,7 +11,7 @@ Package: nvme-stas Architecture: all -Depends: ${python3:Depends}, ${misc:Depends}, python3-pyudev, python3-systemd, python3-gi +Depends: ${python3:Depends}, ${misc:Depends}, python3-pyudev, python3-systemd, python3-gi, python3-netifaces Description: NVMe STorage Appliance Services This package provides two daemons, stafd and stacd. The STorage Appliance Finder Daemon (stafd) automatically discovers NVMe-oF Discovery Controllers (DC) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/docker-compose.yml new/nvme-stas-1.0~rc4/docker-compose.yml --- old/nvme-stas-1.0~rc2/docker-compose.yml 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/docker-compose.yml 2022-03-15 18:30:55.000000000 +0100 @@ -12,7 +12,8 @@ GITHUB_REPO: libnvme GITHUB_TOKEN: volumes: - - /run/dbus:/run/dbus + - /run/dbus:/run/dbus + - /etc/nvme:/etc/nvme privileged: true network_mode: host diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/etc/stas/stacd.conf new/nvme-stas-1.0~rc4/etc/stas/stacd.conf --- old/nvme-stas-1.0~rc2/etc/stas/stacd.conf 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/etc/stas/stacd.conf 2022-03-15 18:30:55.000000000 +0100 @@ -39,6 +39,65 @@ # Unit: Seconds #kato=120 +# ignore-iface: This option controls how connections with I/O Controllers (IOC) +# are made. +# +# There is no guarantee that there will be a route to reach that +# IOC. However, we can use the socket option SO_BINDTODEVICE to +# force the connection to be made on a specific interface instead +# of letting the routing tables decide where to make the connection. +# +# This option determines whether stacd will use SO_BINDTODEVICE to +# force connections on an interface or just rely on the routing +# tables. The default is to use SO_BINDTODEVICE, in other words, +# stacd does not ignore the interface. +# +# BACKGROUND: +# By default, stacd will connect to IOCs on the same interface that +# was used to retrieve the discovery log pages. If stafd discovers +# a DC on an interface using mDNS, and stafd connects to that DC +# and retrieves the log pages, it is expected that the storage +# subsystems listed in the log pages are reachable on the same +# interface where the DC was discovered. +# +# For example, let's say a DC is discovered on interface ens102. +# Then all the subsystems listed in the log pages retrieved from +# that DC must be reachable on interface ens102. If this doesn't +# work, for example you cannot "ping -I ens102 [storage-ip]", then +# the most likely explanation is that arp proxy is not enabled on +# the switch that the host is connected to on interface ens102. +# Whatever you do, resist the temptation to manually set up the +# routing tables or to add alternate routes going over a different +# interface than the one where the DC is located. That simply +# won't work. Make sure arp proxy is enabled on the switch first. +# +# Setting routes won't work because, by default, stacd uses the +# SO_BINDTODEVICE socket option when it connects to IOCs. This +# option is used to force a socket connection to be made on a +# specific interface instead of letting the routing tables decide +# where to connect the socket. Even if you were to manually +# configure an alternate route on a different interface, the +# connections (i.e. host to IOC) will still be made on the +# interface where the DC was discovered by stafd. +# +# Type: boolean +# Range: [false, true] +# Default: true +#ignore-iface=false + +# ip-family: With this you can specify whether stacd will support IPv4, IPv6, +# or both when connecting to I/O Controllers (IOC). stacd will +# not try to connect to IP addresses (whether they come from the +# discovery log pages or manually configured with the 'controller' +# option defined below) if those IP addresses are disabled by this +# option. stacd will default to "ipv4+ipv6" if an invalid value is +# specified for this option. +# +# Type: String +# Range: [ipv4, ipv6, ipv4+ipv6] +# Default: ipv4+ipv6 +#ip-family=ipv4+ipv6 + [Controllers] # controller: I/O Controllers (IOC) are specified with this keyword. # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/etc/stas/stafd.conf new/nvme-stas-1.0~rc4/etc/stas/stafd.conf --- old/nvme-stas-1.0~rc2/etc/stas/stafd.conf 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/etc/stas/stafd.conf 2022-03-15 18:30:55.000000000 +0100 @@ -47,6 +47,42 @@ # Range: [false, true] #persistent-connections=true +# ignore-iface: This option controls how connections with Discovery Controllers +# (DC) are made. +# +# DCs are automatically discovered using DNS-SD/mDNS. mDNS +# provides the DC's IP address and the interface on which the DC +# was discovered. +# +# There is no guarantee that there will be a route to reach that +# DC. However, we can use the socket option SO_BINDTODEVICE to +# force the connection to be made on a specific interface instead +# of letting the routing tables decide where to make the +# connection. +# +# This option determines whether stafd will use SO_BINDTODEVICE to +# force connections on an interface or just rely on the routing +# tables. The default is to use SO_BINDTODEVICE, in other words, +# stafd does not ignore the interface. +# +# Type: boolean +# Range: [false, true] +# Default: false +#ignore-iface=false + +# ip-family: With this you can specify whether stafd will support IPv4, IPv6, +# or both when connecting to Discovery Controllers (DC). stafd will +# not try to connect to IP addresses (whether discovered with mDNS +# or manually configured with the 'controller' option defined below) +# if those IP addresses are disabled by this option. stafd will +# default to "ipv4+ipv6" if an invalid value is specified for this +# option. +# +# Type: String +# Range: [ipv4, ipv6, ipv4+ipv6] +# Default: ipv4+ipv6 +#ip-family=ipv4+ipv6 + [Service Discovery] # zeroconf: Control whether DNS-SD/mDNS automatic discovery is enabled. This is # used to enable or disable automatic discovery of Discovery diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/man/stacd.conf.xml new/nvme-stas-1.0~rc4/man/stacd.conf.xml --- old/nvme-stas-1.0~rc2/man/stacd.conf.xml 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/man/stacd.conf.xml 2022-03-15 18:30:55.000000000 +0100 @@ -83,6 +83,78 @@ <xi:include href="standard-conf.xml" xpointer="hdr-digest"/> <xi:include href="standard-conf.xml" xpointer="data-digest"/> <xi:include href="standard-conf.xml" xpointer="kato"/> + <xi:include href="standard-conf.xml" xpointer="ip-family"/> + + <varlistentry> + <term><varname>ignore-iface=</varname></term> + <listitem> + <para> + Takes a boolean argument. This option controls how + connections with I/O Controllers (IOC) are made. + </para> + + <para> + There is no guarantee that there will be a route to + reach that IOC. However, we can use the socket + option SO_BINDTODEVICE to force the connection to be + made on a specific interface instead of letting the + routing tables decide where to make the connection. + </para> + + <para> + This option determines whether <code>stacd</code> will use + SO_BINDTODEVICE to force connections on an interface + or just rely on the routing tables. The default is + to use SO_BINDTODEVICE, in other words, <code>stacd</code> does + not ignore the interface. + </para> + + <para> + BACKGROUND: + By default, <code>stacd</code> will connect to IOCs on the same + interface that was used to retrieve the discovery + log pages. If stafd discovers a DC on an interface + using mDNS, and stafd connects to that DC and + retrieves the log pages, it is expected that the + storage subsystems listed in the log pages are + reachable on the same interface where the DC was + discovered. + </para> + + <para> + For example, let's say a DC is discovered on + interface ens102. Then all the subsystems listed in + the log pages retrieved from that DC must be + reachable on interface ens102. If this doesn't work, + for example you cannot "ping -I ens102 [storage-ip]", + then the most likely explanation is that arp proxy + is not enabled on the switch that the host is + connected to on interface ens102. Whatever you do, + resist the temptation to manually set up the routing + tables or to add alternate routes going over a + different interface than the one where the DC is + located. That simply won't work. Make sure arp proxy + is enabled on the switch first. + </para> + + <para> + Setting routes won't work because, by default, <code>stacd</code> + uses the SO_BINDTODEVICE socket option when it + connects to IOCs. This option is used to force a + socket connection to be made on a specific interface + instead of letting the routing tables decide where + to connect the socket. Even if you were to manually + configure an alternate route on a different interface, + the connections (i.e. host to IOC) will still be + made on the interface where the DC was discovered by + stafd. + </para> + + <para> + Defaults to <literal>false</literal>. + </para> + </listitem> + </varlistentry> </variablelist> </refsect2> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/man/stafd.conf.xml new/nvme-stas-1.0~rc4/man/stafd.conf.xml --- old/nvme-stas-1.0~rc2/man/stafd.conf.xml 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/man/stafd.conf.xml 2022-03-15 18:30:55.000000000 +0100 @@ -84,6 +84,7 @@ <xi:include href="standard-conf.xml" xpointer="hdr-digest"/> <xi:include href="standard-conf.xml" xpointer="data-digest"/> <xi:include href="standard-conf.xml" xpointer="kato"/> + <xi:include href="standard-conf.xml" xpointer="ip-family"/> <varlistentry> <term><varname>persistent-connections=</varname></term> @@ -95,7 +96,43 @@ even when stafd is stopped. When <literal>false</literal>, <code>stafd</code> will disconnect from all DCs it is connected to on - exit. Defaults to<literal>false</literal>. + exit. Defaults to <literal>false</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>ignore-iface=</varname></term> + <listitem> + <para> + Takes a boolean argument. This option controls how + connections with Discovery Controllers (DC) are made. + </para> + + <para> + DCs are automatically discovered using DNS-SD/mDNS. + mDNS provides the DC's IP address and the interface + on which the DC was discovered. + </para> + + <para> + There is no guarantee that there will be a route to + reach that DC. However, we can use the socket option + SO_BINDTODEVICE to force the connection to be made + on a specific interface instead of letting the + routing tables decide where to make the connection. + </para> + + <para> + This option determines whether <code>stafd</code> + will use SO_BINDTODEVICE to force connections on an + interface or just rely on the routing tables. The + default is to use SO_BINDTODEVICE, in other words, + <code>stafd</code> does not ignore the interface by + default. + </para> + <para> + Defaults to <literal>false</literal>. </para> </listitem> </varlistentry> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/man/standard-conf.xml new/nvme-stas-1.0~rc4/man/standard-conf.xml --- old/nvme-stas-1.0~rc2/man/standard-conf.xml 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/man/standard-conf.xml 2022-03-15 18:30:55.000000000 +0100 @@ -64,6 +64,30 @@ </para> </listitem> </varlistentry> + + <varlistentry id='ip-family'> + <term><varname>ip-family=</varname></term> + + <listitem id='ip-family-text'> + <para> + Takes a string argument. With this you can specify + whether IPv4, IPv6, or both are supported when + connecting to a Controller. Connections will not be + attempted to IP addresses (whether discovered or + manually configured with the 'controller') if those + IP addresses are disabled by this option. If an invalid + value is entered, then "ipv4+ipv6" will be used by default. + </para> + + <para> + Choices are <literal>ipv4</literal>, <literal>ipv6</literal>, or <literal>ipv4+ipv6</literal>. + </para> + + <para> + Defaults to <literal>ipv4+ipv6</literal>. + </para> + </listitem> + </varlistentry> </variablelist> <refsect2 id='controller'> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/meson.build new/nvme-stas-1.0~rc4/meson.build --- old/nvme-stas-1.0~rc2/meson.build 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/meson.build 2022-03-15 18:30:55.000000000 +0100 @@ -17,6 +17,7 @@ ] ) +#=============================================================================== prefix = get_option('prefix') etcdir = get_option('sysconfdir') datadir = get_option('datadir') @@ -28,6 +29,9 @@ want_man = get_option('man') want_html = get_option('html') +# Check for libnvme availability +libnvme_dep = dependency('libnvme', fallback : ['libnvme', 'libnvme_dep']) + # Check that we have all the right Python3 dependencies python3 = import('python').find_installation('python3') python_version = python3.language_version() @@ -39,11 +43,11 @@ check_pymodules = get_option('check_pymodules') if check_pymodules py_modules_reqd = [ - ['libnvme', 'This library must be installed from sources (at this time)'], - ['dasbus', 'Install python3-dasbus (rpm) OR pip3 install dasbus'], - ['pyudev', 'Install python3-pyudev (deb/rpm)'], - ['systemd', 'Install python3-systemd (deb/rpm)'], - ['gi', 'Install python3-gi (deb) OR python3-gobject (rpm)'], + ['dasbus', 'Install python3-dasbus (rpm) OR pip3 install dasbus'], + ['pyudev', 'Install python3-pyudev (deb/rpm)'], + ['netifaces', 'Install python3-netifaces (deb/rpm)'], + ['systemd', 'Install python3-systemd (deb/rpm)'], + ['gi', 'Install python3-gi (deb) OR python3-gobject (rpm)'], ] if want_man or want_html py_modules_reqd += [['lxml', 'Install python3-lxml (deb/rpm)']] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/nvme-stas.spec.in new/nvme-stas-1.0~rc4/nvme-stas.spec.in --- old/nvme-stas-1.0~rc2/nvme-stas.spec.in 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/nvme-stas.spec.in 2022-03-15 18:30:55.000000000 +0100 @@ -12,6 +12,14 @@ BuildRequires: meson BuildRequires: python3-devel +BuildRequires: python3-libnvme +BuildRequires: systemd-rpm-macros +Requires: python3-libnvme +Requires: python3-dasbus +Requires: python3-pyudev +Requires: python3-systemd +Requires: python3-gobject +Requires: python3-netifaces %description NVMe STorage Appliance Services diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/stacctl.py new/nvme-stas-1.0~rc4/stacctl.py --- old/nvme-stas-1.0~rc2/stacctl.py 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/stacctl.py 2022-03-15 18:30:55.000000000 +0100 @@ -70,7 +70,8 @@ try: ARGS.func(ARGS) -except dasbus.error.DBusError as ex: - sys.exit(f'{ex}') -except AttributeError as ex: - sys.exit(f'{ex}') +except dasbus.error.DBusError: + sys.exit(f'Unable to communicate with {defs.STACD_PROCNAME} over D-Bus. Is {defs.STACD_PROCNAME} running?') +except AttributeError: + PARSER.print_help() + sys.exit() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/stacd.py new/nvme-stas-1.0~rc4/stacd.py --- old/nvme-stas-1.0~rc2/stacd.py 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/stacd.py 2022-03-15 18:30:55.000000000 +0100 @@ -89,8 +89,13 @@ SYS_CNF = stas.get_sysconf() # Singleton NVME_ROOT = nvme.root() # Singleton +NVME_ROOT.log_level("debug" if (ARGS.tron or CNF.tron) else "err") NVME_HOST = nvme.host(NVME_ROOT, SYS_CNF.hostnqn, SYS_CNF.hostid, SYS_CNF.hostsymname) # Singleton +def set_loglevel(tron): + stas.trace_control(tron) + NVME_ROOT.log_level("debug" if tron else "err") + #******************************************************************************* class Ioc(stas.Controller): ''' @brief This object establishes a connection to one I/O Controller. @@ -132,7 +137,7 @@ @tron.setter def tron(self, value): # pylint: disable=no-self-use ''' @brief Set Trace ON property ''' - stas.trace_control(value) + set_loglevel(value) @property def log_level(self) -> str: @@ -198,7 +203,7 @@ ''' systemd.daemon.notify('RELOADING=1') CNF.reload() - stas.trace_control(CNF.tron) + set_loglevel(CNF.tron) self._cfg_soak_tmr.start(Stac.CONF_STABILITY_SOAK_TIME_SEC) systemd.daemon.notify('READY=1') return GLib.SOURCE_CONTINUE @@ -218,6 +223,7 @@ LOG.debug('Stac._config_ctrls_finish() - discovered_ctrl_list = %s', discovered_ctrl_list) controllers = stas.remove_blacklisted(configured_ctrl_list + discovered_ctrl_list) + controllers = stas.remove_invalid_addresses(controllers) new_controller_ids = { stas.TransportId(controller) for controller in controllers } cur_controller_ids = set(self._controllers.keys()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/stafctl.py new/nvme-stas-1.0~rc4/stafctl.py --- old/nvme-stas-1.0~rc2/stafctl.py 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/stafctl.py 2022-03-15 18:30:55.000000000 +0100 @@ -96,7 +96,8 @@ try: ARGS.func(ARGS) -except dasbus.error.DBusError as ex: - sys.exit(f'{ex}') -except AttributeError as ex: - sys.exit(f'{ex}') +except dasbus.error.DBusError: + sys.exit(f'Unable to communicate with {defs.STAFD_PROCNAME} over D-Bus. Is {defs.STAFD_PROCNAME} running?') +except AttributeError: + PARSER.print_help() + sys.exit() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/stafd.py new/nvme-stas-1.0~rc4/stafd.py --- old/nvme-stas-1.0~rc2/stafd.py 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/stafd.py 2022-03-15 18:30:55.000000000 +0100 @@ -112,8 +112,13 @@ SYS_CNF = stas.get_sysconf() # Singleton NVME_ROOT = nvme.root() # Singleton +NVME_ROOT.log_level("debug" if (ARGS.tron or CNF.tron) else "err") NVME_HOST = nvme.host(NVME_ROOT, SYS_CNF.hostnqn, SYS_CNF.hostid, SYS_CNF.hostsymname) # Singleton +def set_loglevel(tron): + stas.trace_control(tron) + NVME_ROOT.log_level("debug" if tron else "err") + #******************************************************************************* class Dc(stas.Controller): ''' @brief This object establishes a connection to one Discover Controller (DC). @@ -322,7 +327,7 @@ @tron.setter def tron(self, value): # pylint: disable=no-self-use ''' @brief Set Trace ON property ''' - stas.trace_control(value) + set_loglevel(value) @property def log_level(self) -> str: @@ -398,7 +403,7 @@ ''' systemd.daemon.notify('RELOADING=1') CNF.reload() - stas.trace_control(CNF.tron) + set_loglevel(CNF.tron) self._avahi.config_stypes(CNF.get_stypes()) self._cfg_soak_tmr.start() systemd.daemon.notify('READY=1') @@ -430,6 +435,7 @@ LOG.debug('Staf._config_ctrls_finish() - referral_ctrl_list = %s', referral_ctrl_list) controllers = stas.remove_blacklisted(configured_ctrl_list + discovered_ctrl_list + referral_ctrl_list) + controllers = stas.remove_invalid_addresses(controllers) new_controller_ids = { stas.TransportId(controller) for controller in controllers } cur_controller_ids = set(self._controllers.keys()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/staslib/stas.py new/nvme-stas-1.0~rc4/staslib/stas.py --- old/nvme-stas-1.0~rc2/staslib/stas.py 2022-02-21 18:58:16.000000000 +0100 +++ new/nvme-stas-1.0~rc4/staslib/stas.py 2022-03-15 18:30:55.000000000 +0100 @@ -16,6 +16,8 @@ import logging as LG import configparser import platform +import ipaddress +import netifaces import pyudev import systemd.daemon import dasbus.connection @@ -150,6 +152,8 @@ ('Global', 'hdr-digest'): 'false', ('Global', 'data-digest'): 'false', ('Global', 'kato'): None, + ('Global', 'ignore-iface'): 'false', + ('Global', 'ip-family'): 'ipv4+ipv6', ('Service Discovery', 'zeroconf'): 'enabled', ('Controllers', 'controller'): list(), ('Controllers', 'blacklist'): list(), @@ -163,22 +167,52 @@ self._config = self.read_conf_file() @property + def conf_file(self): + return self._conf_file + + @property def tron(self): ''' @brief return the "tron" config parameter ''' - return self.__get_value('Global', 'tron')[0] == 'true' + return self.__get_bool('Global', 'tron') @property def hdr_digest(self): ''' @brief return the "hdr-digest" config parameter ''' - return self.__get_value('Global', 'hdr-digest')[0] == 'true' + return self.__get_bool('Global', 'hdr-digest') @property def data_digest(self): ''' @brief return the "data-digest" config parameter ''' - return self.__get_value('Global', 'data-digest')[0] == 'true' + return self.__get_bool('Global', 'data-digest') + + @property + def persistent_connections(self): + ''' @brief return the "persistent-connections" config parameter + ''' + return self.__get_bool('Global', 'persistent-connections') + + @property + def ignore_iface(self): + ''' @brief return the "ignore-iface" config parameter + ''' + return self.__get_bool('Global', 'ignore-iface') + + @property + def ip_family(self): + ''' @brief return the "ip-family" config parameter. + @rtype tuple + ''' + family = self.__get_value('Global', 'ip-family')[0] + + if family == 'ipv4': + return (4, ) + if family == 'ipv6': + return (6, ) + + return (4, 6) @property def kato(self): @@ -187,12 +221,6 @@ kato = self.__get_value('Global', 'kato')[0] return None if kato is None else int(kato) - @property - def persistent_connections(self): - ''' @brief return the "persistent-connections" config parameter - ''' - return self.__get_value('Global', 'persistent-connections')[0] == 'true' - def get_controllers(self): ''' @brief Return the list of controllers in the config file. Each controller is in the form of a dictionary as follows. @@ -256,6 +284,9 @@ config.read(self._conf_file) return config + def __get_bool(self, section, option): + return self.__get_value(section, option)[0] == 'true' + def __get_value(self, section, option): try: value = self._config.get(section=section, option=option) @@ -303,6 +334,9 @@ except FileNotFoundError as ex: sys.exit('Error reading mandatory Host NQN (see stasadm --help): %s', ex) + if not value.startswith('nqn.'): + sys.exit('Error Host NQN "%s" should start with "nqn."', value) + return value @property @@ -604,6 +638,13 @@ @return The device if a match is found, None otherwise. ''' for device in self._context.list_devices(subsystem='nvme', NVME_TRADDR=tid.traddr, NVME_TRSVCID=tid.trsvcid, NVME_TRTYPE=tid.transport): + # Note: Prior to 5.18 linux didn't expose the cntrltype through + # the sysfs. So, this may return None on older kernels. + cntrltype = device.attributes.get('cntrltype') + if cntrltype is not None and cntrltype.decode() != 'discovery': + continue + + # Imply Discovery controller based on the absence of children. # Discovery Controllers have no children devices if len(list(device.children)) != 0: continue @@ -617,6 +658,13 @@ def find_nvme_ioc_device(self, tid): for device in self._context.list_devices(subsystem='nvme', NVME_TRADDR=tid.traddr, NVME_TRSVCID=tid.trsvcid, NVME_TRTYPE=tid.transport): + # Note: Prior to 5.18 linux didn't expose the cntrltype through + # the sysfs. So, this may return None on older kernels. + cntrltype = device.attributes.get('cntrltype') + if cntrltype is not None and cntrltype.decode() != 'io': + continue + + # Imply I/O controller based on the presence of children. # I/O Controllers have children devices if len(list(device.children)) == 0: continue @@ -689,6 +737,44 @@ return controllers #******************************************************************************* +def remove_invalid_addresses(controllers:list): + valid_controllers = list() + for controller in controllers: + # First, let's make sure that traddr is + # syntactically a valid IPv4 or IPv6 address. + traddr = controller.get('traddr') + try: + ip = ipaddress.ip_address(traddr) + except ValueError: + LOG.warning('%s IP address is not valid', TransportId(controller)) + continue + + if ip.version not in CNF.ip_family: + LOG.debug('%s ignored because IPv%s is disabled in %s', + TransportId(controller), ip.version, CNF.conf_file) + continue + + # Next, if the interface is not specified, we'll assume that the + # IP address is valid and that there is a route to reach traddr. + iface = controller.get('host-iface') + if not iface: + valid_controllers.append(controller) + else: + # Finally, if there is an interface specified, let's make sure + # that the interface is enabled to connect using traddr. In other + # words, if traddr is an IPv4 address, then the interface must have + # IPv4 enabled. Same logic applies to IPv6. + ifaddresses = netifaces.ifaddresses(iface) # List of IP addresses configured on the interface + if ( (ip.version == 4 and netifaces.AF_INET in ifaddresses) or + (ip.version == 6 and netifaces.AF_INET6 in ifaddresses) ): + valid_controllers.append(controller) + else: + LOG.warning('%s rejected because interface %s is not configured for IPv%s', + TransportId(controller), iface, ip.version) + + return valid_controllers + +#******************************************************************************* class TransportId: # pylint: disable=too-many-instance-attributes ''' Transport Identifier @@ -713,7 +799,7 @@ trsvcid = cid.get('trsvcid') self._trsvcid = trsvcid if trsvcid else (TransportId.RDMA_IP_PORT if self._transport == 'rdma' else TransportId.DISC_IP_PORT) # pylint: disable=used-before-assignment self._host_traddr = cid.get('host-traddr', '') - self._host_iface = cid.get('host-iface', '') + self._host_iface = '' if CNF.ignore_iface else cid.get('host-iface', '') self._subsysnqn = cid.get('subsysnqn') self._key = (self._transport, self._traddr, self._trsvcid, self._host_traddr, self._host_iface, self._subsysnqn) self._hash = hash(self._key) @@ -798,7 +884,7 @@ def addr_resolved(resolver, result, indx): hostname = controllers[indx]['traddr'] - traddr = None + traddr = hostname try: addresses = resolver.lookup_by_name_finish(result) if addresses: @@ -1081,7 +1167,9 @@ def _try_to_connect(self): self._connect_attempts += 1 - host_iface = self.tid.host_iface if self.tid.host_iface and get_nvme_options().host_iface_supp else None + host_iface = self.tid.host_iface if (self.tid.host_iface and + not CNF.ignore_iface and + get_nvme_options().host_iface_supp) else None self._ctrl = nvme.ctrl(self._root, subsysnqn=self.tid.subsysnqn, transport=self.tid.transport, traddr=self.tid.traddr, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nvme-stas-1.0~rc2/subprojects/libnvme.wrap new/nvme-stas-1.0~rc4/subprojects/libnvme.wrap --- old/nvme-stas-1.0~rc2/subprojects/libnvme.wrap 1970-01-01 01:00:00.000000000 +0100 +++ new/nvme-stas-1.0~rc4/subprojects/libnvme.wrap 2022-03-15 18:30:55.000000000 +0100 @@ -0,0 +1,6 @@ +[wrap-git] +url = https://github.com/linux-nvme/libnvme.git +revision = 3a1bc6c6226d240b544058065382e2546018caa8 + +[provide] +libnvme = libnvme_dep ++++++ nvme-stas.obsinfo ++++++ --- /var/tmp/diff_new_pack.0VCu6Z/_old 2022-03-23 20:20:25.270532103 +0100 +++ /var/tmp/diff_new_pack.0VCu6Z/_new 2022-03-23 20:20:25.274532105 +0100 @@ -1,5 +1,5 @@ name: nvme-stas -version: 1.0~rc2 -mtime: 1645466296 -commit: 025e126c568d0103f4d6a26a5392182603b358e3 +version: 1.0~rc4 +mtime: 1647365455 +commit: 3fe9da46822603ff3dafb5c8708a1856580df2e5
