diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/meson.build | 2 | ||||
-rw-r--r-- | tests/nvme_attach_detach_ns_test.py | 2 | ||||
-rw-r--r-- | tests/nvme_copy_test.py | 92 | ||||
-rw-r--r-- | tests/nvme_create_max_ns_test.py | 2 | ||||
-rw-r--r-- | tests/nvme_format_test.py | 2 | ||||
-rw-r--r-- | tests/nvme_get_features_test.py | 2 | ||||
-rw-r--r-- | tests/nvme_test.py | 26 |
7 files changed, 99 insertions, 29 deletions
diff --git a/tests/meson.build b/tests/meson.build index 90f1a68..f8c6aa2 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -95,5 +95,5 @@ if autopep8.found() and isort.found() command : [python, linter_script, 'format'], ) else - message('autopep8 or isort not found. Python formating disabled') + message('autopep8 or isort not found. Python formatting disabled') endif diff --git a/tests/nvme_attach_detach_ns_test.py b/tests/nvme_attach_detach_ns_test.py index 07c118f..075f211 100644 --- a/tests/nvme_attach_detach_ns_test.py +++ b/tests/nvme_attach_detach_ns_test.py @@ -64,7 +64,7 @@ class TestNVMeAttachDetachNSCmd(TestNVMe): Post Section for TestNVMeAttachDetachNSCmd - Create primary namespace. - - Atttach it to controller. + - Attach it to controller. - Call super class's destructor. """ self.assertEqual(self.create_and_validate_ns(self.default_nsid, diff --git a/tests/nvme_copy_test.py b/tests/nvme_copy_test.py index d84a6d2..a547231 100644 --- a/tests/nvme_copy_test.py +++ b/tests/nvme_copy_test.py @@ -4,56 +4,110 @@ # # Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved. # -# Author: Arunpandian J <apj.arun@samsung.com> +# Authors: Arunpandian J <apj.arun@samsung.com> +# Joy Gu <jgu@purestorage.com> """ NVMe Copy Testcase:- 1. Issue copy command on set of block; shall pass. + 2. If cross-namespace copy formats are supported, enable and test + cross-namespace copy formats. """ +import subprocess + from nvme_test import TestNVMe class TestNVMeCopy(TestNVMe): """ - Represents NVMe Verify testcase. + Represents NVMe Copy testcase. - Attributes: - - start_block : starting block of to verify operation. - - range : Range of blocks for DSM operation. - - slbs : 64-bit addr of first block per range + - ocfs : optional copy formats supported + - host_behavior_data : host behavior support data to restore during teardown - test_log_dir : directory for logs, temp files. """ def setUp(self): """ Pre Section for TestNVMeCopy """ super().setUp() - self.start_block = 0 - self.range = 1 - self.slbs = 1 - self.namespace = 1 + print("\nSetting up test...") + self.ocfs = self.get_ocfs() + 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) + # enable cross-namespace copy formats + if self.host_behavior_data[4] & cross_namespace_copy: + # skip if already enabled + print("Cross-namespace copy already enabled, skipping set-features") + 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)) + proc = subprocess.Popen(set_features_cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE) + 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]) 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)) + proc = subprocess.Popen(set_features_cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE) + proc.communicate(input=self.host_behavior_data) super().tearDown() - def copy(self): + def copy(self, sdlba, blocks, slbs, **kwargs): """ Wrapper for nvme copy - Args: - - None + - sdlba : destination logical block address + - blocks : number of logical blocks (0-based) + - slbs : source range logical block address + - descriptor_format : copy descriptor format (optional) + - snsids : source namespace id (optional) + - sopts : source options (optional) - Returns: - - return code for nvme copy command. + - None """ - copy_cmd = "nvme copy " + self.ctrl + \ - " --namespace-id=" + str(self.namespace) + \ - " --sdlba=" + str(self.start_block) + \ - " --blocks=" + str(self.range) + \ - " --slbs=" + str(self.range) - return self.exec_cmd(copy_cmd) + # skip if descriptor format not supported (default format is 0) + desc_format = kwargs.get("descriptor_format", 0) + if not self.ocfs & (1 << desc_format): + 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}" + 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 """ - self.assertEqual(self.copy(), 0) + 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) + self.copy(0, 1, 2, descriptor_format=2, snsids=self.ns1_nsid, sopts=0) + self.copy(0, 1, 2, descriptor_format=3, snsids=self.ns1_nsid) + self.copy(0, 1, 2, descriptor_format=3, snsids=self.ns1_nsid, sopts=0) diff --git a/tests/nvme_create_max_ns_test.py b/tests/nvme_create_max_ns_test.py index 5179711..bda93e1 100644 --- a/tests/nvme_create_max_ns_test.py +++ b/tests/nvme_create_max_ns_test.py @@ -66,7 +66,7 @@ class TestNVMeCreateMaxNS(TestNVMe): Post Section for TestNVMeAttachDetachNSCmd - Create primary namespace. - - Atttach it to controller. + - Attach it to controller. - Call super class's destructor. """ self.assertEqual(self.create_and_validate_ns(self.default_nsid, diff --git a/tests/nvme_format_test.py b/tests/nvme_format_test.py index 68e5a2f..40635c1 100644 --- a/tests/nvme_format_test.py +++ b/tests/nvme_format_test.py @@ -81,7 +81,7 @@ class TestNVMeFormatCmd(TestNVMe): Post Section for TestNVMeFormatCmd - Create primary namespace. - - Atttach it to controller. + - Attach it to controller. - Call super class's destructor. """ self.assertEqual(self.create_and_validate_ns(self.default_nsid, diff --git a/tests/nvme_get_features_test.py b/tests/nvme_get_features_test.py index 784f2be..974fc34 100644 --- a/tests/nvme_get_features_test.py +++ b/tests/nvme_get_features_test.py @@ -47,7 +47,7 @@ class TestNVMeGetMandatoryFeatures(TestNVMe): - Attributes: - feature_id_list : list of the mandatory features. - get_vector_list_cmd : vector list collection for 09h. - - vector_list_len : numer of the interrupt vectors. + - vector_list_len : number of the interrupt vectors. """ def setUp(self): diff --git a/tests/nvme_test.py b/tests/nvme_test.py index d5eca18..0df3dac 100644 --- a/tests/nvme_test.py +++ b/tests/nvme_test.py @@ -39,7 +39,7 @@ from nvme_test_logger import TestNVMeLogger class TestNVMe(unittest.TestCase): """ - Represents a testcase, each testcase shuold inherit this + Represents a testcase, each testcase should inherit this class or appropriate subclass which is a child of this class. Common utility functions used in various testcases. @@ -58,11 +58,13 @@ class TestNVMe(unittest.TestCase): self.ctrl = "XXX" self.ns1 = "XXX" self.test_log_dir = "XXX" + self.do_validate_pci_device = True self.default_nsid = 0x1 self.config_file = 'tests/config.json' self.load_config() - self.validate_pci_device() + if self.do_validate_pci_device: + self.validate_pci_device() def tearDown(self): """ Post Section for TestNVMe. """ @@ -70,7 +72,7 @@ class TestNVMe(unittest.TestCase): shutil.rmtree(self.log_dir, ignore_errors=True) def validate_pci_device(self): - """ Validate underlaying device belogs to pci subsystem. + """ Validate underlying device belongs to pci subsystem. - Args: - None - Returns: @@ -93,6 +95,7 @@ class TestNVMe(unittest.TestCase): 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.clear_log_dir = False if self.clear_log_dir is True: @@ -162,7 +165,7 @@ class TestNVMe(unittest.TestCase): return ctrl_id def get_ns_list(self): - """ Wrapper for extrating the namespace list. + """ Wrapper for extracting the namespace list. - Args: - None - Returns: @@ -181,7 +184,7 @@ class TestNVMe(unittest.TestCase): return ns_list def get_max_ns(self): - """ Wrapper for extracting maximum number of namspaces supported. + """ Wrapper for extracting maximum number of namespaces supported. - Args: - None - Returns: @@ -228,6 +231,19 @@ class TestNVMe(unittest.TestCase): print(ncap) return int(ncap) + def get_ocfs(self): + """ Wrapper for extracting optional copy formats supported + - Args: + - None + - Returns: + - Optional Copy Formats Supported + """ + pattern = re.compile(r'^ocfs\s*: 0x[0-9a-fA-F]+$') + output = subprocess.check_output(["nvme", "id-ctrl", self.ctrl], encoding='utf-8') + ocfs_line = next(line for line in output.splitlines() if pattern.match(line)) + ocfs = ocfs_line.split(":")[1].strip() + return int(ocfs, 16) + def get_format(self): """ Wrapper for extracting format. - Args: |