diff --git a/.github/workflows/run-nightly-tests.yml b/.github/workflows/run-nightly-tests.yml index d525986a21..58fc2a7712 100644 --- a/.github/workflows/run-nightly-tests.yml +++ b/.github/workflows/run-nightly-tests.yml @@ -9,39 +9,25 @@ on: jobs: nightly-tests: runs-on: nvme-nvm + container: + image: ghcr.io/linux-nvme/debian.python:latest + #Expose all devices to the container through the `privileged` flag. + # + #BDEV0 is an environment variable of the self-hosted runner instance + #that contains a valid nvme ctrl name which is capable of the nvm + #command set. + options: '--privileged -e BDEV0' steps: - name: Output kernel version run: | uname -a - - name: Clean up test device - run: | - #BDEV0 is an environment variable of the self-hosted runner instance - #that contains a valid nvme ctrl name which is capable of the nvm - #command set. - CONTROLLER=$(echo /dev/${BDEV0} | sed 's/n[0-9]*$//') - sudo nvme delete-ns $CONTROLLER -n 0xffffffff - sudo nvme format $CONTROLLER -n 0xffffffff -l 0 -f - SIZE=$(sudo nvme id-ctrl $CONTROLLER --output-format=json | jq -r '{tnvmcap} | .[]' | awk '{print $1/512}') - sudo nvme create-ns -s $SIZE -c $SIZE -f 0 -d 0 --csi=0 $CONTROLLER - sudo nvme attach-ns $CONTROLLER -n 1 -c 0 - uses: actions/checkout@v4 - name: Install dependencies run: | - sudo apt-get update - sudo apt-get install --no-install-recommends -y \ - meson gcc pkg-config git libjson-c-dev libssl-dev libkeyutils-dev \ - libdbus-1-dev libpython3-dev pipx python3-dev swig xz-utils - pipx ensurepath - sudo PIPX_BIN_DIR=/usr/local/bin pipx install nose2 - sudo PIPX_BIN_DIR=/usr/local/bin pipx install flake8 - sudo PIPX_BIN_DIR=/usr/local/bin pipx install mypy - sudo PIPX_BIN_DIR=/usr/local/bin pipx install autopep8 - sudo PIPX_BIN_DIR=/usr/local/bin pipx install isort + PIPX_BIN_DIR=/usr/local/bin pipx install nose2 --force - name: Build and install nvme-cli run: | scripts/build.sh -b release -c gcc - sudo meson install -C .build-ci - sudo ldconfig /usr/local/lib64 - name: Overwrite test config run: | CONTROLLER=$(echo /dev/${BDEV0} | sed 's/n[0-9]*$//') @@ -49,12 +35,14 @@ jobs: { "controller" : "$CONTROLLER", "ns1": "/dev/${BDEV0}", - "log_dir": "tests/nvmetests/" + "log_dir": "tests/nvmetests/", + "nvme_bin": "$(pwd)/.build-ci/nvme" } EOF + cat tests/config.json - name: Run on device tests run: | - sudo nose2 --verbose --start-dir tests \ + nose2 --verbose --start-dir tests \ nvme_attach_detach_ns_test \ nvme_compare_test \ nvme_copy_test \ @@ -79,15 +67,6 @@ jobs: uses: actions/upload-artifact@v4 if: always() with: - name: logs files + name: nvme-cli-test-logs path: | ./tests/nvmetests/**/*.log - - name: Clean up test device - if: always() - run: | - CONTROLLER=$(echo /dev/${BDEV0} | sed 's/n[0-9]*$//') - sudo nvme delete-ns $CONTROLLER -n 0xffffffff - sudo nvme format $CONTROLLER -n 0xffffffff -l 0 -f - SIZE=$(sudo nvme id-ctrl $CONTROLLER --output-format=json | jq -r '{tnvmcap} | .[]' | awk '{print $1/512}') - sudo nvme create-ns -s $SIZE -c $SIZE -f 0 -d 0 --csi=0 $CONTROLLER - sudo nvme attach-ns $CONTROLLER -n 1 -c 0 diff --git a/tests/nvme_attach_detach_ns_test.py b/tests/nvme_attach_detach_ns_test.py index a0e6129d5d..f23630087a 100644 --- a/tests/nvme_attach_detach_ns_test.py +++ b/tests/nvme_attach_detach_ns_test.py @@ -29,8 +29,6 @@ 5. Delete Namespace. """ -import time - from nvme_test import TestNVMe @@ -59,7 +57,6 @@ def setUp(self): self.setup_log_dir(self.__class__.__name__) self.ctrl_id = self.get_ctrl_id() self.delete_all_ns() - time.sleep(1) def tearDown(self): """ diff --git a/tests/nvme_compare_test.py b/tests/nvme_compare_test.py index a34df68e30..e6b2b75ab9 100644 --- a/tests/nvme_compare_test.py +++ b/tests/nvme_compare_test.py @@ -78,10 +78,10 @@ def nvme_compare(self, cmp_file): - Returns: - return code of the nvme compare command. """ - compare_cmd = "nvme compare " + self.ns1 + " --start-block=" + \ - str(self.start_block) + " --block-count=" + \ - str(self.block_count) + " --data-size=" + \ - str(self.data_size) + " --data=" + cmp_file + compare_cmd = f"{self.nvme_bin} compare {self.ns1} " + \ + f"--start-block={str(self.start_block)} " + \ + f"--block-count={str(self.block_count)} " + \ + f"--data-size={str(self.data_size)} --data={cmp_file}" return self.exec_cmd(compare_cmd) def test_nvme_compare(self): diff --git a/tests/nvme_copy_test.py b/tests/nvme_copy_test.py index 5d16b00dda..f73cd04b1e 100644 --- a/tests/nvme_copy_test.py +++ b/tests/nvme_copy_test.py @@ -34,15 +34,20 @@ class TestNVMeCopy(TestNVMe): def setUp(self): """ Pre Section for TestNVMeCopy """ super().setUp() - print("\nSetting up test...") self.ocfs = self.get_ocfs() self.host_behavior_data = None cross_namespace_copy = self.ocfs & 0xc if cross_namespace_copy: # get host behavior support data - get_features_cmd = ["nvme", "get-feature", self.ctrl, "--feature-id=0x16", "--data-len=512", "-b"] - print("Running command:", " ".join(get_features_cmd)) - self.host_behavior_data = subprocess.check_output(get_features_cmd) + get_features_cmd = f"{self.nvme_bin} get-feature {self.ctrl} " + \ + "--feature-id=0x16 --data-len=512 --raw-binary" + proc = subprocess.Popen(get_features_cmd, + shell=True, + stdout=subprocess.PIPE, + encoding='utf-8') + err = proc.wait() + self.assertEqual(err, 0, "ERROR : nvme get-feature failed") + self.host_behavior_data = proc.stdout.read() # enable cross-namespace copy formats if self.host_behavior_data[4] & cross_namespace_copy: # skip if already enabled @@ -50,29 +55,37 @@ def setUp(self): self.host_behavior_data = None else: data = self.host_behavior_data[:4] + cross_namespace_copy.to_bytes(2, 'little') + self.host_behavior_data[6:] - set_features_cmd = ["nvme", "set-feature", self.ctrl, "--feature-id=0x16", "--data-len=512"] - print("Running command:", " ".join(set_features_cmd)) + set_features_cmd = f"{self.nvme_bin} set-feature " + \ + f"{self.ctrl} --feature-id=0x16 --data-len=512" proc = subprocess.Popen(set_features_cmd, + shell=True, stdout=subprocess.PIPE, - stdin=subprocess.PIPE) + stdin=subprocess.PIPE, + encoding='utf-8') proc.communicate(input=data) self.assertEqual(proc.returncode, 0, "Failed to enable cross-namespace copy formats") - get_ns_id_cmd = ["nvme", "get-ns-id", self.ns1] - print("Running command:", " ".join(get_ns_id_cmd)) - output = subprocess.check_output(get_ns_id_cmd) - self.ns1_nsid = int(output.decode().strip().split(':')[-1]) + get_ns_id_cmd = f"{self.nvme_bin} get-ns-id {self.ns1}" + proc = subprocess.Popen(get_ns_id_cmd, + shell=True, + stdout=subprocess.PIPE, + encoding='utf-8') + err = proc.wait() + self.assertEqual(err, 0, "ERROR : nvme get-ns-id failed") + output = proc.stdout.read() + self.ns1_nsid = int(output.strip().split(':')[-1]) self.setup_log_dir(self.__class__.__name__) def tearDown(self): """ Post Section for TestNVMeCopy """ - print("Tearing down test...") if self.host_behavior_data: # restore saved host behavior support data - set_features_cmd = ["nvme", "set-feature", self.ctrl, "--feature-id=0x16", "--data-len=512"] - print("Running command:", " ".join(set_features_cmd)) + set_features_cmd = f"{self.nvme_bin} set-feature {self.ctrl} " + \ + "--feature-id=0x16 --data-len=512" proc = subprocess.Popen(set_features_cmd, + shell=True, stdout=subprocess.PIPE, - stdin=subprocess.PIPE) + stdin=subprocess.PIPE, + encoding='utf-8') proc.communicate(input=self.host_behavior_data) super().tearDown() @@ -94,18 +107,18 @@ def copy(self, sdlba, blocks, slbs, **kwargs): print(f"Skip copy because descriptor format {desc_format} is not supported") return # build copy command - copy_cmd = f"nvme copy {self.ns1} --format={desc_format} --sdlba={sdlba} --blocks={blocks} --slbs={slbs}" + copy_cmd = f"{self.nvme_bin} copy {self.ns1} " + \ + f"--format={desc_format} --sdlba={sdlba} --blocks={blocks} " + \ + f"--slbs={slbs}" if "snsids" in kwargs: copy_cmd += f" --snsids={kwargs['snsids']}" if "sopts" in kwargs: copy_cmd += f" --sopts={kwargs['sopts']}" # run and assert success - print("Running command:", copy_cmd) self.assertEqual(self.exec_cmd(copy_cmd), 0) def test_copy(self): """ Testcase main """ - print("Running test...") self.copy(0, 1, 2, descriptor_format=0) self.copy(0, 1, 2, descriptor_format=1) self.copy(0, 1, 2, descriptor_format=2, snsids=self.ns1_nsid) diff --git a/tests/nvme_create_max_ns_test.py b/tests/nvme_create_max_ns_test.py index 76cd4d4a10..f4fa72c7bb 100644 --- a/tests/nvme_create_max_ns_test.py +++ b/tests/nvme_create_max_ns_test.py @@ -29,8 +29,6 @@ 5. Delete all Namespaces. """ -import time - from nvme_test import TestNVMe @@ -63,7 +61,6 @@ def setUp(self): self.max_ns = self.get_max_ns() self.ctrl_id = self.get_ctrl_id() self.delete_all_ns() - time.sleep(1) def tearDown(self): """ @@ -85,21 +82,21 @@ def test_attach_detach_ns(self): """ Testcase main """ print(f"##### Testing max_ns: {self.max_ns}") for nsid in range(1, self.max_ns + 1): - print("##### Creating " + str(nsid)) + print(f"##### Creating {nsid}") err = self.create_and_validate_ns(nsid, self.nsze, self.ncap, self.flbas, self.dps) self.assertEqual(err, 0) - print("##### Attaching " + str(nsid)) + print(f"##### Attaching {nsid}") self.assertEqual(self.attach_ns(self.ctrl_id, nsid), 0) - print("##### Running IOs in " + str(nsid)) + print(f"##### Running IOs in {nsid}") self.run_ns_io(nsid, 9, 1) for nsid in range(1, self.max_ns + 1): - print("##### Detaching " + str(nsid)) + print(f"##### Detaching {nsid}") self.assertEqual(self.detach_ns(self.ctrl_id, nsid), 0) - print("#### Deleting " + str(nsid)) + print(f"#### Deleting {nsid}") self.assertEqual(self.delete_and_validate_ns(nsid), 0) self.nvme_reset_ctrl() diff --git a/tests/nvme_ctrl_reset_test.py b/tests/nvme_ctrl_reset_test.py index e2c8a00636..f32042e8a8 100644 --- a/tests/nvme_ctrl_reset_test.py +++ b/tests/nvme_ctrl_reset_test.py @@ -40,7 +40,7 @@ def ctrl_reset(self): - Returns: - return code for nvme controller reset. """ - ctrl_reset_cmd = "nvme reset " + self.ctrl + ctrl_reset_cmd = f"{self.nvme_bin} reset {self.ctrl}" return self.exec_cmd(ctrl_reset_cmd) def test_ctrl_reset(self): diff --git a/tests/nvme_dsm_test.py b/tests/nvme_dsm_test.py index d92bf58e59..4ad3e05ed5 100644 --- a/tests/nvme_dsm_test.py +++ b/tests/nvme_dsm_test.py @@ -45,10 +45,9 @@ def dsm(self): - Returns: - return code for nvme dsm command. """ - dsm_cmd = "nvme dsm " + self.ctrl + \ - " --namespace-id=" + str(self.namespace) + \ - " --blocks=" + str(self.range) + \ - " --slbs=" + str(self.start_block) + dsm_cmd = f"{self.nvme_bin} dsm {self.ctrl} " + \ + f"--namespace-id={str(self.namespace)} " + \ + f"--blocks={str(self.range)} --slbs={str(self.start_block)}" return self.exec_cmd(dsm_cmd) def test_dsm(self): diff --git a/tests/nvme_flush_test.py b/tests/nvme_flush_test.py index e4f127dac1..0f2d3c52a8 100644 --- a/tests/nvme_flush_test.py +++ b/tests/nvme_flush_test.py @@ -53,8 +53,8 @@ def nvme_flush(self): - Returns: - None """ - flush_cmd = "nvme flush " + self.ctrl + " -n " + str(self.default_nsid) - print(flush_cmd) + flush_cmd = f"{self.nvme_bin} flush {self.ctrl} " + \ + f"--namespace-id={str(self.default_nsid)}" return self.exec_cmd(flush_cmd) def test_nvme_flush(self): diff --git a/tests/nvme_format_test.py b/tests/nvme_format_test.py index 12bc128f9a..68445ac843 100644 --- a/tests/nvme_format_test.py +++ b/tests/nvme_format_test.py @@ -37,9 +37,9 @@ - Delete Namespace. """ +import json import math import subprocess -import time from nvme_test import TestNVMe @@ -55,9 +55,7 @@ class TestNVMeFormatCmd(TestNVMe): - nsze : namespace size. - ncap : namespace capacity. - ctrl_id : controller id. - - lba_format_list : lis of supported format. - - ms_list : list of metadat size per format. - - lbads_list : list of LBA data size per format. + - lba_format_list : json list of supported format. - test_log_dir : directory for logs, temp files. """ @@ -74,12 +72,9 @@ def setUp(self): self.nsze = ncap self.ctrl_id = self.get_ctrl_id() self.lba_format_list = [] - self.ms_list = [] - self.lbads_list = [] self.test_log_dir = self.log_dir + "/" + self.__class__.__name__ self.setup_log_dir(self.__class__.__name__) self.delete_all_ns() - time.sleep(1) def tearDown(self): """ @@ -106,50 +101,44 @@ def attach_detach_primary_ns(self): self.dps), 0) self.assertEqual(self.attach_ns(self.ctrl_id, self.default_nsid), 0) # read lbaf information - id_ns = "nvme id-ns " + self.ctrl + \ - " -n1 | grep ^lbaf | awk '{print $2}' | tr -s \"\\n\" \" \"" - proc = subprocess.Popen(id_ns, shell=True, stdout=subprocess.PIPE, + id_ns_cmd = f"{self.nvme_bin} id-ns {self.ctrl} " + \ + f"--namespace-id={self.default_nsid} --output-format=json" + proc = subprocess.Popen(id_ns_cmd, + shell=True, + stdout=subprocess.PIPE, encoding='utf-8') - self.lba_format_list = proc.stdout.read().strip().split(" ") - if proc.wait() == 0: - # read lbads information - id_ns = "nvme id-ns " + self.ctrl + \ - " -n1 | grep ^lbaf | awk '{print $5}'" + \ - " | cut -f 2 -d ':' | tr -s \"\\n\" \" \"" - proc = subprocess.Popen(id_ns, shell=True, stdout=subprocess.PIPE, - encoding='utf-8') - self.lbads_list = proc.stdout.read().strip().split(" ") - # read metadata information - id_ns = "nvme id-ns " + self.ctrl + \ - " -n1 | grep ^lbaf | awk '{print $4}'" + \ - " | cut -f 2 -d ':' | tr -s \"\\n\" \" \"" - proc = subprocess.Popen(id_ns, shell=True, stdout=subprocess.PIPE, - encoding='utf-8') - self.ms_list = proc.stdout.read().strip().split(" ") - self.assertEqual(self.detach_ns(self.ctrl_id, self.default_nsid), 0) - self.assertEqual(self.delete_and_validate_ns(self.default_nsid), 0) - self.nvme_reset_ctrl() + err = proc.wait() + self.assertEqual(err, 0, "ERROR : nvme id-ns failed") + json_output = json.loads(proc.stdout.read()) + self.lba_format_list = json_output['lbafs'] + self.assertTrue(len(self.lba_format_list) > 0, + "ERROR : nvme id-ns could not find any lba formats") + self.assertEqual(self.detach_ns(self.ctrl_id, self.default_nsid), 0) + self.assertEqual(self.delete_and_validate_ns(self.default_nsid), 0) + self.nvme_reset_ctrl() def test_format_ns(self): """ Testcase main """ # extract the supported format information. self.attach_detach_primary_ns() + print("##### Testing lba formats:") # iterate through all supported format - for i in range(0, len(self.lba_format_list)): - print("\nlba format " + str(self.lba_format_list[i]) + - " lbad " + str(self.lbads_list[i]) + - " ms " + str(self.ms_list[i])) - metadata_size = 1 if self.ms_list[i] == '8' else 0 + for flbas, lba_format in enumerate(self.lba_format_list): + ds = lba_format['ds'] + ms = lba_format['ms'] + print(f"\nlba format {str(flbas)}" + f"\nds {str(ds)}" + f"\nms {str(ms)}") + dps = 1 if str(ms) == '8' else 0 err = self.create_and_validate_ns(self.default_nsid, self.nsze, self.ncap, - self.lba_format_list[i], - metadata_size) + flbas, + dps) self.assertEqual(err, 0) self.assertEqual(self.attach_ns(self.ctrl_id, self.default_nsid), 0) - self.run_ns_io(self.default_nsid, self.lbads_list[i]) - time.sleep(5) + self.run_ns_io(self.default_nsid, int(ds)) self.assertEqual(self.detach_ns(self.ctrl_id, self.default_nsid), 0) self.assertEqual(self.delete_and_validate_ns(self.default_nsid), 0) self.nvme_reset_ctrl() diff --git a/tests/nvme_fw_log_test.py b/tests/nvme_fw_log_test.py index b67067170c..e56953df8b 100644 --- a/tests/nvme_fw_log_test.py +++ b/tests/nvme_fw_log_test.py @@ -57,16 +57,12 @@ def get_fw_log(self): - Returns: - 0 on success, error code on failure. """ - err = 0 - fw_log_cmd = "nvme fw-log " + self.ctrl + fw_log_cmd = f"{self.nvme_bin} fw-log {self.ctrl}" proc = subprocess.Popen(fw_log_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') - fw_log_output = proc.communicate()[0] - print("\n" + fw_log_output + "\n") - err = proc.wait() - return err + return proc.wait() def test_fw_log(self): """ Testcase main """ diff --git a/tests/nvme_get_features_test.py b/tests/nvme_get_features_test.py index fc3e5bfeb1..d1bf6211f7 100644 --- a/tests/nvme_get_features_test.py +++ b/tests/nvme_get_features_test.py @@ -81,27 +81,23 @@ def get_mandatory_features(self, feature_id): """ if str(feature_id) == "0x09": for vector in range(self.vector_list_len): - get_feat_cmd = "nvme get-feature " + self.ctrl + \ - " --feature-id=" + str(feature_id) + \ - " --cdw11=" + str(vector) + " -H" + get_feat_cmd = f"{self.nvme_bin} get-feature {self.ctrl} " + \ + f"--feature-id={str(feature_id)} " + \ + f"--cdw11={str(vector)} --human-readable" proc = subprocess.Popen(get_feat_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') - feature_output = proc.communicate()[0] - print(feature_output) self.assertEqual(proc.wait(), 0) else: - get_feat_cmd = "nvme get-feature " + self.ctrl + \ - " --feature-id=" + str(feature_id) + " -H" + get_feat_cmd = f"{self.nvme_bin} get-feature {self.ctrl} " + \ + f"--feature-id={str(feature_id)} --human-readable" if str(feature_id) == "0x05": get_feat_cmd += f" --namespace-id={self.default_nsid}" proc = subprocess.Popen(get_feat_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') - feature_output = proc.communicate()[0] - print(feature_output) self.assertEqual(proc.wait(), 0) def test_get_mandatory_features(self): diff --git a/tests/nvme_get_lba_status_test.py b/tests/nvme_get_lba_status_test.py index d9e543c5a4..15842cfba8 100644 --- a/tests/nvme_get_lba_status_test.py +++ b/tests/nvme_get_lba_status_test.py @@ -50,21 +50,17 @@ def get_lba_status(self): - Returns: - 0 on success, error code on failure. """ - err = 0 - get_lba_status_cmd = "nvme get-lba-status " + self.ctrl + \ - " --namespace-id=" + str(self.ns1) + \ - " --start-lba=" + str(self.start_lba) + \ - " --max-dw=" + str(self.max_dw) + \ - " --action=" + str(self.action) + \ - " --range-len=" + str(self.range_len) + get_lba_status_cmd = f"{self.nvme_bin} get-lba-status {self.ctrl} " + \ + f"--namespace-id={str(self.ns1)} " + \ + f"--start-lba={str(self.start_lba)} " + \ + f"--max-dw={str(self.max_dw)} " + \ + f"--action={str(self.action)} " + \ + f"--range-len={str(self.range_len)}" proc = subprocess.Popen(get_lba_status_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') - get_lba_status_output = proc.communicate()[0] - print("\n" + get_lba_status_output + "\n") - err = proc.wait() - return err + return proc.wait() def test_get_lba_status(self): """ Testcase main """ diff --git a/tests/nvme_id_ns_test.py b/tests/nvme_id_ns_test.py index 66e2f93adc..46ed3eeffe 100644 --- a/tests/nvme_id_ns_test.py +++ b/tests/nvme_id_ns_test.py @@ -42,7 +42,7 @@ def setUp(self): """ Pre Section for TestNVMeIdentifyNamespace. """ super().setUp() self.setup_log_dir(self.__class__.__name__) - self.ns_list = self.get_ns_list() + self.nsid_list = self.get_nsid_list() def tearDown(self): """ @@ -60,16 +60,13 @@ def get_id_ns(self, nsid): - Returns: - 0 on success, error code on failure. """ - err = 0 - id_ns_cmd = "nvme id-ns " + self.ctrl + "n" + str(nsid) + id_ns_cmd = f"{self.nvme_bin} id-ns {self.ctrl} " + \ + f"--namespace-id={str(nsid)}" proc = subprocess.Popen(id_ns_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') - id_ns_output = proc.communicate()[0] - print(id_ns_output + "\n") - err = proc.wait() - return err + return proc.wait() def get_id_ns_all(self): """ @@ -80,7 +77,7 @@ def get_id_ns_all(self): - 0 on success, error code on failure. """ err = 0 - for namespace in self.ns_list: + for namespace in self.nsid_list: err = self.get_id_ns(str(namespace)) return err diff --git a/tests/nvme_lba_status_log_test.py b/tests/nvme_lba_status_log_test.py index a50e211cec..079fe5c71c 100644 --- a/tests/nvme_lba_status_log_test.py +++ b/tests/nvme_lba_status_log_test.py @@ -45,16 +45,12 @@ def get_lba_stat_log(self): - Returns: - 0 on success, error code on failure. """ - err = 0 - lba_stat_log_cmd = "nvme lba-status-log " + self.ctrl + lba_stat_log_cmd = f"{self.nvme_bin} lba-status-log {self.ctrl}" proc = subprocess.Popen(lba_stat_log_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') - lba_stat_log_output = proc.communicate()[0] - print("\n" + lba_stat_log_output + "\n") - err = proc.wait() - return err + return proc.wait() def test_lba_stat_log(self): """ Testcase main """ diff --git a/tests/nvme_smart_log_test.py b/tests/nvme_smart_log_test.py index 196998b860..ebc076c10d 100644 --- a/tests/nvme_smart_log_test.py +++ b/tests/nvme_smart_log_test.py @@ -76,14 +76,14 @@ def get_smart_log_all_ns(self): - Returns: - 0 on success, error code on failure. """ - ns_list = self.get_ns_list() - for nsid in range(0, len(ns_list)): - self.get_smart_log_ns(ns_list[nsid]) + nsid_list = self.get_nsid_list() + for nsid in nsid_list: + self.get_smart_log_ns(nsid) return 0 def test_smart_log(self): """ Testcase main """ self.assertEqual(self.get_smart_log_ctrl(), 0) - smlp = self.supp_check_id_ctrl("lpa") + smlp = int(self.get_id_ctrl_field_value("lpa"), 16) if smlp & 0x1: self.assertEqual(self.get_smart_log_all_ns(), 0) diff --git a/tests/nvme_test.py b/tests/nvme_test.py index a13a9939f0..863fc023d4 100644 --- a/tests/nvme_test.py +++ b/tests/nvme_test.py @@ -30,7 +30,6 @@ import stat import subprocess import sys -import time import unittest from nvme_test_logger import TestNVMeLogger @@ -58,19 +57,27 @@ def setUp(self): self.ctrl = "XXX" self.ns1 = "XXX" self.test_log_dir = "XXX" + self.nvme_bin = "nvme" self.do_validate_pci_device = True self.default_nsid = 0x1 + self.flbas = 0 self.config_file = 'tests/config.json' self.load_config() if self.do_validate_pci_device: self.validate_pci_device() + print(f"\nsetup: ctrl: {self.ctrl}, ns1: {self.ns1}, default_nsid: {self.default_nsid}, flbas: {self.flbas}\n") def tearDown(self): """ Post Section for TestNVMe. """ if self.clear_log_dir is True: shutil.rmtree(self.log_dir, ignore_errors=True) self.create_and_attach_default_ns() + print(f"\nteardown: ctrl: {self.ctrl}, ns1: {self.ns1}, default_nsid: {self.default_nsid}, flbas: {self.flbas}\n") + + @classmethod + def tearDownClass(cls): + print("\n") def create_and_attach_default_ns(self): """ Creates a default namespace with the full capacity of the ctrls NVM @@ -103,8 +110,8 @@ def validate_pci_device(self): - None """ x1, x2, dev = self.ctrl.split('/') - cmd = cmd = "find /sys/devices -name \\*" + dev + " | grep -i pci" - err = subprocess.call(cmd, shell=True) + cmd = "find /sys/devices -name \\*" + dev + " | grep -i pci" + err = subprocess.call(cmd, shell=True, stdout=subprocess.DEVNULL) self.assertEqual(err, 0, "ERROR : Only NVMe PCI subsystem is supported") def load_config(self): @@ -119,7 +126,10 @@ def load_config(self): self.ctrl = config['controller'] self.ns1 = config['ns1'] self.log_dir = config['log_dir'] - self.do_validate_pci_device = config.get('do_validate_pci_device', self.do_validate_pci_device) + self.nvme_bin = config.get('nvme_bin', self.nvme_bin) + print(f"\nUsing nvme binary '{self.nvme_bin}'") + self.do_validate_pci_device = config.get( + 'do_validate_pci_device', self.do_validate_pci_device) self.clear_log_dir = False if self.clear_log_dir is True: @@ -154,20 +164,17 @@ def nvme_reset_ctrl(self): - Returns: - None """ - nvme_reset_cmd = "nvme reset " + self.ctrl + nvme_reset_cmd = f"{self.nvme_bin} reset {self.ctrl}" err = subprocess.call(nvme_reset_cmd, shell=True, - stdout=subprocess.PIPE, - encoding='utf-8') + stdout=subprocess.DEVNULL) self.assertEqual(err, 0, "ERROR : nvme reset failed") - time.sleep(5) rescan_cmd = "echo 1 > /sys/bus/pci/rescan" proc = subprocess.Popen(rescan_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8') - time.sleep(5) self.assertEqual(proc.wait(), 0, "ERROR : pci rescan failed") def get_ctrl_id(self): @@ -177,7 +184,8 @@ def get_ctrl_id(self): - Returns: - controller id. """ - get_ctrl_id = f"nvme list-ctrl {self.ctrl} --output-format=json" + get_ctrl_id = f"{self.nvme_bin} list-ctrl {self.ctrl} " + \ + "--output-format=json" proc = subprocess.Popen(get_ctrl_id, shell=True, stdout=subprocess.PIPE, @@ -189,7 +197,7 @@ def get_ctrl_id(self): "ERROR : nvme list-ctrl could not find ctrl") return str(json_output['ctrl_list'][0]['ctrl_id']) - def get_ns_list(self): + def get_nsid_list(self): """ Wrapper for extracting the namespace list. - Args: - None @@ -197,14 +205,17 @@ def get_ns_list(self): - List of the namespaces. """ ns_list = [] - ns_list_cmd = "nvme list-ns " + self.ctrl + ns_list_cmd = f"{self.nvme_bin} list-ns {self.ctrl} " + \ + "--output-format=json" proc = subprocess.Popen(ns_list_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') self.assertEqual(proc.wait(), 0, "ERROR : nvme list namespace failed") - for line in proc.stdout: - ns_list.append(line.split('x')[-1]) + json_output = json.loads(proc.stdout.read()) + + for ns in json_output['nsid_list']: + ns_list.append(ns['nsid']) return ns_list @@ -215,22 +226,16 @@ def get_max_ns(self): - Returns: - maximum number of namespaces supported. """ - pattern = re.compile("^nn[ ]+: [0-9]", re.IGNORECASE) - max_ns = -1 - max_ns_cmd = "nvme id-ctrl " + self.ctrl + max_ns_cmd = f"{self.nvme_bin} id-ctrl {self.ctrl} " + \ + "--output-format=json" proc = subprocess.Popen(max_ns_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') err = proc.wait() self.assertEqual(err, 0, "ERROR : reading maximum namespace count failed") - - for line in proc.stdout: - if pattern.match(line): - max_ns = line.split(":")[1].strip() - break - print(max_ns) - return int(max_ns) + json_output = json.loads(proc.stdout.read()) + return int(json_output['nn']) def get_lba_status_supported(self): """ Check if 'Get LBA Status' command is supported by the device @@ -248,7 +253,8 @@ def get_lba_format_size(self): - Returns: - lba format size as a tuple of (data_size, metadata_size) in bytes. """ - nvme_id_ns_cmd = f"nvme id-ns {self.ns1} --output-format=json" + nvme_id_ns_cmd = f"{self.nvme_bin} id-ns {self.ns1} " + \ + "--output-format=json" proc = subprocess.Popen(nvme_id_ns_cmd, shell=True, stdout=subprocess.PIPE, @@ -274,24 +280,9 @@ def get_ncap(self): - Args: - None - Returns: - - maximum number of namespaces supported. + - Total NVM capacity. """ - pattern = re.compile("^tnvmcap[ ]+: [0-9]", re.IGNORECASE) - ncap = -1 - ncap_cmd = "nvme id-ctrl " + self.ctrl - proc = subprocess.Popen(ncap_cmd, - shell=True, - stdout=subprocess.PIPE, - encoding='utf-8') - err = proc.wait() - self.assertEqual(err, 0, "ERROR : reading nvm capacity failed") - - for line in proc.stdout: - if pattern.match(line): - ncap = line.split(":")[1].strip() - break - print(ncap) - return int(ncap) + return int(self.get_id_ctrl_field_value("tnvmcap")) def get_id_ctrl_field_value(self, field): """ Wrapper for extracting id-ctrl field values @@ -300,7 +291,8 @@ def get_id_ctrl_field_value(self, field): - Returns: - Filed value of the given field """ - id_ctrl_cmd = f"nvme id-ctrl {self.ctrl} --output-format=json" + id_ctrl_cmd = f"{self.nvme_bin} id-ctrl {self.ctrl} " + \ + "--output-format=json" proc = subprocess.Popen(id_ctrl_cmd, shell=True, stdout=subprocess.PIPE, @@ -330,7 +322,8 @@ def get_format(self): """ # defaulting to 4K nvm_format = 4096 - nvm_format_cmd = "nvme id-ns " + self.ctrl + " -n1" + nvm_format_cmd = f"{self.nvme_bin} id-ns {self.ctrl} " + \ + f"--namespace-id={self.default_nsid}" proc = subprocess.Popen(nvm_format_cmd, shell=True, stdout=subprocess.PIPE, @@ -338,10 +331,11 @@ def get_format(self): err = proc.wait() self.assertEqual(err, 0, "ERROR : reading nvm capacity failed") + # Not using json output here because parsing flbas makes this less + # readable as the format index is split into lower and upper bits for line in proc.stdout: if "in use" in line: nvm_format = 2 ** int(line.split(":")[3].split()[0]) - print(nvm_format) return int(nvm_format) def delete_all_ns(self): @@ -351,15 +345,19 @@ def delete_all_ns(self): - Returns: - None """ - delete_ns_cmd = "nvme delete-ns " + self.ctrl + " -n 0xFFFFFFFF" + delete_ns_cmd = f"{self.nvme_bin} delete-ns {self.ctrl} " + \ + "--namespace-id=0xFFFFFFFF" self.assertEqual(self.exec_cmd(delete_ns_cmd), 0) - list_ns_cmd = "nvme list-ns " + self.ctrl + " --all | wc -l" + list_ns_cmd = f"{self.nvme_bin} list-ns {self.ctrl} --all " + \ + "--output-format=json" proc = subprocess.Popen(list_ns_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') - output = proc.stdout.read().strip() - self.assertEqual(output, '0', "ERROR : deleting all namespace failed") + self.assertEqual(proc.wait(), 0, "ERROR : nvme list-ns failed") + json_output = json.loads(proc.stdout.read()) + self.assertEqual(len(json_output['nsid_list']), 0, + "ERROR : deleting all namespace failed") def create_ns(self, nsze, ncap, flbas, dps): """ Wrapper for creating a namespace. @@ -371,9 +369,9 @@ def create_ns(self, nsze, ncap, flbas, dps): - Returns: - return code of the nvme create namespace command. """ - create_ns_cmd = "nvme create-ns " + self.ctrl + " --nsze=" + \ - str(nsze) + " --ncap=" + str(ncap) + \ - " --flbas=" + str(flbas) + " --dps=" + str(dps) + create_ns_cmd = f"{self.nvme_bin} create-ns {self.ctrl} " + \ + f"--nsze={str(nsze)} --ncap={str(ncap)} --flbas={str(flbas)} " + \ + f"--dps={str(dps)}" return self.exec_cmd(create_ns_cmd) def create_and_validate_ns(self, nsid, nsze, ncap, flbas, dps): @@ -389,12 +387,11 @@ def create_and_validate_ns(self, nsid, nsze, ncap, flbas, dps): """ err = self.create_ns(nsze, ncap, flbas, dps) if err == 0: - time.sleep(2) - id_ns_cmd = "nvme id-ns " + self.ctrl + " -n " + str(nsid) + id_ns_cmd = f"{self.nvme_bin} id-ns {self.ctrl} " + \ + f"--namespace-id={str(nsid)}" err = subprocess.call(id_ns_cmd, shell=True, - stdout=subprocess.PIPE, - encoding='utf-8') + stdout=subprocess.DEVNULL) return err def attach_ns(self, ctrl_id, ns_id): @@ -405,18 +402,14 @@ def attach_ns(self, ctrl_id, ns_id): - Returns: - 0 on success, error code on failure. """ - attach_ns_cmd = "nvme attach-ns " + self.ctrl + \ - " --namespace-id=" + str(ns_id) + \ - " --controllers=" + ctrl_id + attach_ns_cmd = f"{self.nvme_bin} attach-ns {self.ctrl} " + \ + f"--namespace-id={str(ns_id)} --controllers={ctrl_id}" err = subprocess.call(attach_ns_cmd, shell=True, - stdout=subprocess.PIPE, - encoding='utf-8') - time.sleep(5) + stdout=subprocess.DEVNULL) if err == 0: # enumerate new namespace block device self.nvme_reset_ctrl() - time.sleep(5) # check if new namespace block device exists err = 0 if stat.S_ISBLK(os.stat(self.ns1).st_mode) else 1 return err @@ -429,13 +422,11 @@ def detach_ns(self, ctrl_id, nsid): - Returns: - 0 on success, error code on failure. """ - detach_ns_cmd = "nvme detach-ns " + self.ctrl + \ - " --namespace-id=" + str(nsid) + \ - " --controllers=" + ctrl_id + detach_ns_cmd = f"{self.nvme_bin} detach-ns {self.ctrl} " + \ + f"--namespace-id={str(nsid)} --controllers={ctrl_id}" return subprocess.call(detach_ns_cmd, shell=True, - stdout=subprocess.PIPE, - encoding='utf-8') + stdout=subprocess.DEVNULL) def delete_and_validate_ns(self, nsid): """ Wrapper for deleting and validating that namespace is deleted. @@ -445,11 +436,11 @@ def delete_and_validate_ns(self, nsid): - 0 on success, 1 on failure. """ # delete the namespace - delete_ns_cmd = "nvme delete-ns " + self.ctrl + " -n " + str(nsid) + delete_ns_cmd = f"{self.nvme_bin} delete-ns {self.ctrl} " + \ + f"--namespace-id={str(nsid)}" err = subprocess.call(delete_ns_cmd, shell=True, - stdout=subprocess.PIPE, - encoding='utf-8') + stdout=subprocess.DEVNULL) self.assertEqual(err, 0, "ERROR : delete namespace failed") return err @@ -460,16 +451,14 @@ def get_smart_log(self, nsid): - Returns: - 0 on success, error code on failure. """ - smart_log_cmd = "nvme smart-log " + self.ctrl + " -n " + str(nsid) - print(smart_log_cmd) + smart_log_cmd = f"{self.nvme_bin} smart-log {self.ctrl} " + \ + f"--namespace-id={str(nsid)}" proc = subprocess.Popen(smart_log_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') err = proc.wait() self.assertEqual(err, 0, "ERROR : nvme smart log failed") - smart_log_output = proc.communicate()[0] - print(f"{smart_log_output}") return err def get_id_ctrl(self, vendor=False): @@ -480,10 +469,10 @@ def get_id_ctrl(self, vendor=False): - 0 on success, error code on failure. """ if not vendor: - id_ctrl_cmd = "nvme id-ctrl " + self.ctrl + id_ctrl_cmd = f"{self.nvme_bin} id-ctrl {self.ctrl}" else: - id_ctrl_cmd = "nvme id-ctrl --vendor-specific " + self.ctrl - print(id_ctrl_cmd) + id_ctrl_cmd = f"{self.nvme_bin} id-ctrl " +\ + f"--vendor-specific {self.ctrl}" proc = subprocess.Popen(id_ctrl_cmd, shell=True, stdout=subprocess.PIPE, @@ -500,13 +489,14 @@ def get_error_log(self): - 0 on success, error code on failure. """ pattern = re.compile(r"^ Entry\[[ ]*[0-9]+\]") - error_log_cmd = "nvme error-log " + self.ctrl + error_log_cmd = f"{self.nvme_bin} error-log {self.ctrl}" proc = subprocess.Popen(error_log_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') err = proc.wait() self.assertEqual(err, 0, "ERROR : nvme error log failed") + # This sanity checkes the 'normal' output line = proc.stdout.readline() err_log_entry_count = int(line.split(" ")[5].strip().split(":")[1]) entry_count = 0 @@ -523,42 +513,20 @@ def run_ns_io(self, nsid, lbads, count=10): - Returns: - None """ - block_size = mmap.PAGESIZE if int(lbads) < 9 else 2 ** int(lbads) + (ds, _) = self.get_lba_format_size() + block_size = ds if int(lbads) < 9 else 2 ** int(lbads) ns_path = self.ctrl + "n" + str(nsid) io_cmd = "dd if=" + ns_path + " of=/dev/null" + " bs=" + \ str(block_size) + " count=" + str(count) + " > /dev/null 2>&1" - print(io_cmd) + print(f"Running io: {io_cmd}") run_io = subprocess.Popen(io_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') run_io_result = run_io.communicate()[1] self.assertEqual(run_io_result, None) io_cmd = "dd if=/dev/zero of=" + ns_path + " bs=" + \ str(block_size) + " count=" + str(count) + " > /dev/null 2>&1" - print(io_cmd) + print(f"Running io: {io_cmd}") run_io = subprocess.Popen(io_cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8') run_io_result = run_io.communicate()[1] self.assertEqual(run_io_result, None) - - def supp_check_id_ctrl(self, key): - """ Wrapper for support check. - - Args: - - key : search key. - - Returns: - - value for key requested. - """ - id_ctrl = "nvme id-ctrl " + self.ctrl - print("\n" + id_ctrl) - proc = subprocess.Popen(id_ctrl, - shell=True, - stdout=subprocess.PIPE, - encoding='utf-8') - err = proc.wait() - self.assertEqual(err, 0, "ERROR : nvme Identify controller Data \ - structure failed") - for line in proc.stdout: - if key in line: - key = line.replace(",", "", 1) - print(key) - val = (key.split(':'))[1].strip() - return int(val, 16) diff --git a/tests/nvme_test_io.py b/tests/nvme_test_io.py index bf30e0a4b0..6fac9db0f5 100644 --- a/tests/nvme_test_io.py +++ b/tests/nvme_test_io.py @@ -77,10 +77,10 @@ def nvme_write(self): - Returns: - return code for nvme write command. """ - write_cmd = "nvme write " + self.ns1 + " --start-block=" + \ - str(self.start_block) + " --block-count=" + \ - str(self.block_count) + " --data-size=" + \ - str(self.data_size) + " --data=" + self.write_file + write_cmd = f"{self.nvme_bin} write {self.ns1} " + \ + f"--start-block={str(self.start_block)} " + \ + f"--block-count={str(self.block_count)} " + \ + f"--data-size={str(self.data_size)} --data={self.write_file}" return self.exec_cmd(write_cmd) def nvme_read(self): @@ -90,9 +90,8 @@ def nvme_read(self): - Returns: - return code for nvme read command. """ - read_cmd = "nvme read " + self.ns1 + " --start-block=" + \ - str(self.start_block) + " --block-count=" + \ - str(self.block_count) + " --data-size=" + \ - str(self.data_size) + " --data=" + self.read_file - print(read_cmd) + read_cmd = f"{self.nvme_bin} read {self.ns1} " + \ + f"--start-block={str(self.start_block)} " + \ + f"--block-count={str(self.block_count)} " + \ + f"--data-size={str(self.data_size)} --data={self.read_file}" return self.exec_cmd(read_cmd) diff --git a/tests/nvme_verify_test.py b/tests/nvme_verify_test.py index 7c30828f51..44c8942bff 100644 --- a/tests/nvme_verify_test.py +++ b/tests/nvme_verify_test.py @@ -44,10 +44,10 @@ def verify(self): - Returns: - return code for nvme verify command. """ - verify_cmd = "nvme verify " + self.ctrl + \ - " --namespace-id=" + str(self.namespace) + \ - " --start-block=" + str(self.start_block) + \ - " --block-count=" + str(self.block_count) + verify_cmd = f"{self.nvme_bin} verify {self.ctrl} " + \ + f"--namespace-id={str(self.namespace)} " + \ + f"--start-block={str(self.start_block)} " + \ + f"--block-count={str(self.block_count)}" return self.exec_cmd(verify_cmd) def test_verify(self): diff --git a/tests/nvme_writeuncor_test.py b/tests/nvme_writeuncor_test.py index 1083d46234..8e0b9a05b4 100644 --- a/tests/nvme_writeuncor_test.py +++ b/tests/nvme_writeuncor_test.py @@ -63,9 +63,9 @@ def write_uncor(self): - Returns: - return code of nvme write uncorrectable command. """ - write_uncor_cmd = "nvme write-uncor " + self.ns1 + \ - " --start-block=" + str(self.start_block) + \ - " --block-count=" + str(self.block_count) + write_uncor_cmd = f"{self.nvme_bin} write-uncor {self.ns1} " + \ + f"--start-block={str(self.start_block)} " + \ + f"--block-count={str(self.block_count)}" return self.exec_cmd(write_uncor_cmd) def test_write_uncor(self): diff --git a/tests/nvme_writezeros_test.py b/tests/nvme_writezeros_test.py index 3231e3dd9f..75d5687427 100644 --- a/tests/nvme_writezeros_test.py +++ b/tests/nvme_writezeros_test.py @@ -71,9 +71,9 @@ def write_zeroes(self): - Returns: - return code for nvme write command. """ - write_zeroes_cmd = "nvme write-zeroes " + self.ns1 + \ - " --start-block=" + str(self.start_block) + \ - " --block-count=" + str(self.block_count) + write_zeroes_cmd = f"{self.nvme_bin} write-zeroes {self.ns1} " + \ + f"--start-block={str(self.start_block)} " + \ + f"--block-count={str(self.block_count)}" return self.exec_cmd(write_zeroes_cmd) def validate_write_read(self):