[PATCH] Unittest framework based on nvme-cli.

Chaitanya Kulkarni ckulkarnilinux at gmail.com
Wed Oct 26 23:48:37 PDT 2016


From: Chaitanya Kulkarni <ckulkarnilinux at gmail.com>


Signed-off-by: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
---
 Makefile                            |   5 +-
 tests/Makefile                      |  48 +++++
 tests/README                        |  84 ++++++++
 tests/TODO                          |  14 ++
 tests/config.json                   |   5 +
 tests/nvme_attach_detach_ns_test.py |  90 ++++++++
 tests/nvme_compare_test.py          |  79 ++++++++
 tests/nvme_create_max_ns_test.py    |  97 +++++++++
 tests/nvme_error_log_test.py        |  86 ++++++++
 tests/nvme_flush_test.py            |  61 ++++++
 tests/nvme_format_test.py           | 145 +++++++++++++
 tests/nvme_get_features_test.py     | 103 ++++++++++
 tests/nvme_read_write_test.py       |  72 +++++++
 tests/nvme_simple_template_test.py  |  55 +++++
 tests/nvme_smart_log_test.py        |  86 ++++++++
 tests/nvme_test.py                  | 395 ++++++++++++++++++++++++++++++++++++
 tests/nvme_test_io.py               |  99 +++++++++
 tests/nvme_test_logger.py           |  52 +++++
 tests/nvme_writeuncor_test.py       |  76 +++++++
 tests/nvme_writezeros_test.py       | 102 ++++++++++
 20 files changed, 1753 insertions(+), 1 deletion(-)
 create mode 100644 tests/Makefile
 create mode 100644 tests/README
 create mode 100644 tests/TODO
 create mode 100644 tests/config.json
 create mode 100644 tests/nvme_attach_detach_ns_test.py
 create mode 100644 tests/nvme_compare_test.py
 create mode 100644 tests/nvme_create_max_ns_test.py
 create mode 100644 tests/nvme_error_log_test.py
 create mode 100644 tests/nvme_flush_test.py
 create mode 100644 tests/nvme_format_test.py
 create mode 100644 tests/nvme_get_features_test.py
 create mode 100644 tests/nvme_read_write_test.py
 create mode 100644 tests/nvme_simple_template_test.py
 create mode 100644 tests/nvme_smart_log_test.py
 create mode 100644 tests/nvme_test.py
 create mode 100644 tests/nvme_test_io.py
 create mode 100644 tests/nvme_test_logger.py
 create mode 100644 tests/nvme_writeuncor_test.py
 create mode 100644 tests/nvme_writezeros_test.py

diff --git a/Makefile b/Makefile
index 117cbbe..33c7190 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,9 @@ nvme.o: nvme.c nvme.h nvme-print.h nvme-ioctl.h argconfig.h suffix.h nvme-lightn
 doc: $(NVME)
 	$(MAKE) -C Documentation
 
+test:
+	$(MAKE) -C tests/ run
+
 all: doc
 
 clean:
@@ -136,4 +139,4 @@ rpm: dist
 	$(RPMBUILD) -ta nvme-$(NVME_VERSION).tar.gz
 
 .PHONY: default doc all clean clobber install-man install-bin install
-.PHONY: dist pkg dist-orig deb deb-light rpm FORCE
+.PHONY: dist pkg dist-orig deb deb-light rpm FORCE test
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..c0f9f31
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,48 @@
+###############################################################################
+#
+#    Makefile : Allows user to run testcases, generate documentation, and
+#               perform static code analysis.
+#
+###############################################################################
+
+NOSE2_OPTIONS="--verbose"
+
+help: all
+
+all:
+	@echo "Usage:"
+	@echo
+	@echo "  make run         - Run all testcases."
+	@echo "  make doc         - Generate Documentation."
+	@echo "  make cleanall    - removes *pyc, documentation."
+	@echo "  make static_check- runs pep8, flake8, pylint on code."
+
+doc:
+	@epydoc -v --output=Documentation *.py
+
+run:
+	@for i in `ls *.py`; \
+	do \
+		echo "Running $${i}"; \
+		TESTCASE_NAME=`echo $${i} | cut -f 1 -d '.'`; \
+		nose2 ${NOSE2_OPTIONS} $${TESTCASE_NAME}; \
+	done
+
+static_check:
+	@for i in `ls *.py`; \
+	do \
+		echo "Pylint :- " ; \
+		printf "%10s    " $${i}; \
+		pylint $${i} 2>&1  | grep "^Your code" |  awk '{print $$7}';\
+		echo "--------------------------------------------";\
+		pep8 $${i}; \
+		echo "pep8 :- "; \
+		echo "flake8 :- "; \
+		flake8 $${i}; \
+	done
+
+cleanall: clean
+	@rm -fr *.pyc Documentation
+
+clean:
+	@rm -fr *.pyc
diff --git a/tests/README b/tests/README
new file mode 100644
index 0000000..70686d8
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,84 @@
+nvmetests
+=========
+
+    This contains NVMe unit tests framework. The purpose of this framework
+    to use nvme cli and test various supported commands and scenarios for
+    NVMe device.
+
+    In current implementation this framework uses nvme cli to
+    interact with underlying controller/namespace.
+
+1. Common Package Dependencies
+------------------------------
+
+    1. Python(>= 2.7.5 or >= 3.3)
+    2. nose2(Installation guide http://nose2.readthedocs.io/)
+    3. nvme-cli(https://github.com/linux-nvme/nvme-cli.git)
+
+2. Overview
+-----------
+
+    This framework follows simple class hierarchy. Each test file contains
+    one test. Each test is direct subclass or indirect subclass of TestNVMe
+    class which represents one testcase. To write a new testcase one can copy
+    existing template "nvme_simple_template_test.py" and start adding new
+    testcase specific functionality. For detailed information please look into
+    section 3.
+
+    For more information about tests, class hierarchy and code please refer :-
+
+    1. Documentation :- html/
+    2. Class Index :-  html/index.html
+    3. Class Hierarchy :- html/class-tree.html
+
+    For each testcase it will create log directory mentioned in
+    configuration file. This directory will be used for a temporary files
+    and storing execution logs of each testcases. Current implementation stores
+    stdout and stderr for each testcase under log directory, e.g. :-
+
+        $ tree nvmetests/
+        nvmetests/
+        ├── TestNVMeAttachDetachNSCmd
+        │   ├── stderr.log
+        │   └── stdout.log
+        ├── TestNVMeFlushCmd
+        │   ├── stderr.log
+        │   └── stdout.log
+        └── TestNVMeFormatCmd
+            ├── stderr.log
+            └── stdout.log
+                 .
+                 .
+                 .
+
+3. Walk-Through Example for writing a new testcase
+--------------------------------------------------
+    1. Copy simple test template file from current directory
+       with appropriate name, replace "simple_template" with testcase name
+       in new file name. Update config.json if necessary.
+    2. Write a testcase main function, make sure its name is starting with
+       test_*.
+    3. Based on the requirement one can inherit TestNVMe or TestNVMeIO
+       class.
+    4. Write test precondition code into __init__. Make sure you are calling
+       super class __init__.
+    5. Write test post condition code into __del__. Make sure you are calling
+       super class __del__.
+    6. Before writing a new function have a look into TestNVMe to see if it
+       can be reused.
+    7. Once testcase is ready make sure :-
+           a. Run pep8, flake8, pylint on the testcase and fix errors/warnings.
+              -Example "$ make static_check" will run pep8, flake8 and pylint on
+              all the python files in current directory.
+           b. Execute make doc to generate the documentation.
+              -Example "$ make doc" will create and update existing
+              documentation.
+
+4. Running testcases with framework
+-----------------------------------
+    1. Running single testcase with nose2 :-
+       $ nose2 --verbose nvme_writezeros_test
+       $ nose2 --verbose nvme_read_write_test
+
+    2. Running all the testcases with Makefile :-
+       $ make run
diff --git a/tests/TODO b/tests/TODO
new file mode 100644
index 0000000..69806a9
--- /dev/null
+++ b/tests/TODO
@@ -0,0 +1,14 @@
+nvmetests TODO List
+===================
+
+Feature list (with priority):-
+------------------------------
+    1. PRE and POST section improvements :-
+        a. Add functionality to load and unload driver.
+        b. Make sure default namespace is present, if not create one before
+           any test begins. Read the default namespace size from config file.
+    2. Add system statistics collection in PRE and POST section of testcase.
+    3. Create execution summary file under log directory at the end of each
+       run.
+    4. Add tracing functionality to track overall and current progress of the
+       testcase.
diff --git a/tests/config.json b/tests/config.json
new file mode 100644
index 0000000..098fba8
--- /dev/null
+++ b/tests/config.json
@@ -0,0 +1,5 @@
+{
+	"controller" : "/dev/nvme0",
+        "ns1": "/dev/nvme0n1",
+	"log_dir": "nvmetests/"
+}
diff --git a/tests/nvme_attach_detach_ns_test.py b/tests/nvme_attach_detach_ns_test.py
new file mode 100644
index 0000000..92a82dd
--- /dev/null
+++ b/tests/nvme_attach_detach_ns_test.py
@@ -0,0 +1,90 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+NVMe Namespace Management Testcase:-
+
+    1. Create Namespace and validate.
+    2. Attach Namespace to controller.
+    3. Run IOs on Namespace under test.
+    4. Detach Namespace from controller.
+    5. Delete Namespace.
+"""
+
+import time
+from nose.tools import assert_equal
+from nvme_test import TestNVMe
+
+
+class TestNVMeAttachDetachNSCmd(TestNVMe):
+
+    """
+    Represents Attach, Detach namespace testcase.
+
+        - Attributes:
+              - dps : data protection information.
+              - flabs : LBA format information.
+              - nsze : namespace size.
+              - ncap : namespace capacity.
+              - ctrl_id : controller id.
+    """
+
+    def __init__(self):
+        """ Pre Section for TestNVMeAttachDetachNSCmd """
+        TestNVMe.__init__(self)
+        self.dps = 0
+        self.flbas = 0
+        self.nsze = 0x1400000
+        self.ncap = 0x1400000
+        self.setup_log_dir(self.__class__.__name__)
+        self.ctrl_id = self.get_ctrl_id()
+        self.delete_all_ns()
+        time.sleep(1)
+
+    def __del__(self):
+        """
+        Post Section for TestNVMeAttachDetachNSCmd
+
+            - Create primary namespace.
+            - Atttach it to controller.
+            - Call super class's destructor.
+        """
+        assert_equal(self.create_and_validate_ns(self.default_nsid,
+                                                 self.nsze,
+                                                 self.ncap,
+                                                 self.flbas,
+                                                 self.dps), 0)
+        self.attach_ns(self.ctrl_id, self.default_nsid)
+        TestNVMe.__del__(self)
+
+    def test_attach_detach_ns(self):
+        """ Testcase main """
+        err = self.create_and_validate_ns(self.default_nsid,
+                                          self.nsze,
+                                          self.ncap,
+                                          self.flbas,
+                                          self.dps)
+        assert_equal(err, 0)
+        assert_equal(self.attach_ns(self.ctrl_id, self.default_nsid), 0)
+
+        self.run_ns_io(self.default_nsid, 0)
+
+        assert_equal(self.detach_ns(self.ctrl_id, self.default_nsid), 0)
+        assert_equal(self.delete_and_validate_ns(self.default_nsid), 0)
+        self.nvme_reset_ctrl()
diff --git a/tests/nvme_compare_test.py b/tests/nvme_compare_test.py
new file mode 100644
index 0000000..34c140d
--- /dev/null
+++ b/tests/nvme_compare_test.py
@@ -0,0 +1,79 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+NVMe Compare Command Testcase:-
+
+    1. Create a data file 1 with pattern 1515 to write.
+    2. Create a data file 2 with pattern 2525 to compare with.
+    3. Write a block of data pattern using data file1.
+    4. Compare written block to data file 2's pattern; shall fail.
+    5. Compare written block to data file1's pattern; shall pass.
+
+"""
+
+from nose.tools import assert_equal, assert_not_equal
+from nvme_test_io import TestNVMeIO
+
+
+class TestNVMeCompareCmd(TestNVMeIO):
+
+    """
+    Represents Compare Testcase. Inherits TestNVMeIO class.
+
+        - Attributes:
+              - data_size : data size to perform IO.
+              - start_block : starting block of to perform IO.
+              - compare_file : data file to use in nvme comapre commmand.
+              - test_log_dir : directory for logs, temp files.
+    """
+
+    def __init__(self):
+        """ Pre Section for TestNVMeCompareCmd """
+        TestNVMeIO.__init__(self)
+        self.data_size = 1024
+        self.start_block = 1023
+        self.setup_log_dir(self.__class__.__name__)
+        self.compare_file = self.test_log_dir + "/" + "compare_file.txt"
+        self.write_file = self.test_log_dir + "/" + self.write_file
+        self.create_data_file(self.write_file, self.data_size, "15")
+        self.create_data_file(self.compare_file, self.data_size, "25")
+
+    def __del__(self):
+        """ Post Section for TestNVMeCompareCmd """
+        TestNVMeIO.__del__(self)
+
+    def nvme_compare(self, cmp_file):
+        """ Wrapper for nvme compare command.
+           - Args:
+               - cmp_file : data file used in nvme compare command.
+           - 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
+        return self.exec_cmd(compare_cmd)
+
+    def test_nvme_compare(self):
+        """ Testcase main """
+        assert_equal(self.nvme_write(), 0)
+        assert_not_equal(self.nvme_compare(self.compare_file), 0)
+        assert_equal(self.nvme_compare(self.write_file), 0)
diff --git a/tests/nvme_create_max_ns_test.py b/tests/nvme_create_max_ns_test.py
new file mode 100644
index 0000000..15e0770
--- /dev/null
+++ b/tests/nvme_create_max_ns_test.py
@@ -0,0 +1,97 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+NVMe Namespace Management Testcase:-
+
+    1. Create Maximum number of Namespaces and validate.
+    2. Attach all Namespaces to controller.
+    3. Run IOs on Namespace under test.
+    4. Detach Maximum number of Namespaces from controller.
+    5. Delete all Namespaces.
+"""
+
+import time
+from nose.tools import assert_equal
+from nvme_test import TestNVMe
+
+
+class TestNVMeCreateMaxNS(TestNVMe):
+
+    """
+    Represents Attach, Detach namespace testcase.
+
+        - Attributes:
+              - dps : data protection information.
+              - flabs : LBA format information.
+              - nsze : namespace size.
+              - ncap : namespace capacity.
+              - ctrl_id : controller id.
+    """
+
+    def __init__(self):
+        """ Pre Section for TestNVMeAttachDetachNSCmd """
+        TestNVMe.__init__(self)
+        self.dps = 0
+        self.flbas = 0
+        self.nsze = 0x1400000
+        self.ncap = 0x1400000
+        self.setup_log_dir(self.__class__.__name__)
+        self.max_ns = self.get_max_ns()
+        self.ctrl_id = self.get_ctrl_id()
+        self.delete_all_ns()
+        time.sleep(1)
+
+    def __del__(self):
+        """
+        Post Section for TestNVMeAttachDetachNSCmd
+
+            - Create primary namespace.
+            - Atttach it to controller.
+            - Call super class's destructor.
+        """
+        assert_equal(self.create_and_validate_ns(self.default_nsid,
+                                                 self.nsze,
+                                                 self.ncap,
+                                                 self.flbas,
+                                                 self.dps), 0)
+        self.attach_ns(self.ctrl_id, self.default_nsid)
+        TestNVMe.__del__(self)
+
+    def test_attach_detach_ns(self):
+        """ Testcase main """
+        for nsid in range(1, self.max_ns):
+            print "##### Creating " + str(nsid)
+            err = self.create_and_validate_ns(nsid,
+                                              self.nsze,
+                                              self.ncap,
+                                              self.flbas,
+                                              self.dps)
+            assert_equal(err, 0)
+            print "##### Attaching " + str(nsid)
+            assert_equal(self.attach_ns(self.ctrl_id, nsid), 0)
+            print "##### Running IOs in " + str(nsid)
+            self.run_ns_io(nsid, 0)
+
+        for nsid in range(1, self.max_ns):
+            print "##### Detaching " + str(nsid)
+            assert_equal(self.detach_ns(self.ctrl_id, nsid), 0)
+            print "#### Deleting " + str(nsid)
+            assert_equal(self.delete_and_validate_ns(nsid), 0)
+        self.nvme_reset_ctrl()
diff --git a/tests/nvme_error_log_test.py b/tests/nvme_error_log_test.py
new file mode 100644
index 0000000..cedb24b
--- /dev/null
+++ b/tests/nvme_error_log_test.py
@@ -0,0 +1,86 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+NVMe Smart Log Verification Testcase:-
+
+    1. Execute error-log on controller.
+    2. Execute error-log on each available namespace.
+
+"""
+
+from nose.tools import assert_equal
+from nvme_test import TestNVMe
+
+
+class TestNVMeErrorLogCmd(TestNVMe):
+
+    """
+    Represents Smart Log testcae.
+
+        - Attributes:
+    """
+
+    def __init__(self):
+        """ Pre Section for TestNVMeErrorLogCmd """
+        TestNVMe.__init__(self)
+        self.setup_log_dir(self.__class__.__name__)
+
+    def __del__(self):
+        """
+        Post Section for TestNVMeErrorLogCmd
+
+            - Call super class's destructor.
+        """
+        TestNVMe.__del__(self)
+
+    def get_error_log_ctrl(self):
+        """ Wrapper for executing error-log on controller.
+            - Args:
+                - None:
+            - Returns:
+                - 0 on success, error code on failure.
+        """
+        return self.get_error_log("0xFFFFFFFF")
+
+    def get_error_log_ns(self, nsid):
+        """ Wrapper for executing error-log on a namespace.
+            - Args:
+                - nsid: namespace id to be used in error-log command.
+            - Returns:
+                - 0 on success, error code on failure.
+        """
+        return self.get_error_log(nsid)
+
+    def get_error_log_all_ns(self):
+        """ Wrapper for executing error-log on all the namespaces.
+            - Args:
+                - None:
+            - Returns:
+                - 0 on success, error code on failure.
+        """
+        ns_list = self.get_ns_list()
+        for nsid in range(0, len(ns_list)):
+            self.get_error_log_ns(ns_list[nsid])
+        return 0
+
+    def test_get_error_log(self):
+        """ Testcase main """
+        assert_equal(self.get_error_log_ctrl(), 0)
+        assert_equal(self.get_error_log_all_ns(), 0)
diff --git a/tests/nvme_flush_test.py b/tests/nvme_flush_test.py
new file mode 100644
index 0000000..d080887
--- /dev/null
+++ b/tests/nvme_flush_test.py
@@ -0,0 +1,61 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+NVMe Flush Command Testcase:-
+
+    1. Execute nvme flush on controller.
+
+"""
+
+from nose.tools import assert_equal
+from nvme_test import TestNVMe
+
+
+class TestNVMeFlushCmd(TestNVMe):
+
+    """
+    Represents Flush Testcase. Inherits TestNVMe class.
+
+        - Attributes:
+    """
+
+    def __init__(self):
+        """ Pre Section for TestNVMeFlushCmd """
+        TestNVMe.__init__(self)
+        self.setup_log_dir(self.__class__.__name__)
+
+    def __del__(self):
+        """ Post Section for TestNVMeFlushCmd """
+        TestNVMe.__del__(self)
+
+    def nvme_flush(self):
+        """ Wrapper for nvme flush command.
+           - Args:
+               - None
+           - Returns:
+               - None
+        """
+        flush_cmd = "nvme flush " + self.ctrl + " -n " + str(self.default_nsid)
+        print flush_cmd
+        return self.exec_cmd(flush_cmd)
+
+    def test_nvme_flush(self):
+        """ Testcase main """
+        assert_equal(self.nvme_flush(), 0)
diff --git a/tests/nvme_format_test.py b/tests/nvme_format_test.py
new file mode 100644
index 0000000..e5da23f
--- /dev/null
+++ b/tests/nvme_format_test.py
@@ -0,0 +1,145 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+Namespace Format testcase :-
+
+    1. Create, attach, detach, delete primary namespace and
+       extract the supported format information from default namespace:-
+           - List of the supported format.
+           - List of Metadata Size per format. Based on this we calculate
+             data protection parameter at the time of namespace.
+           - List of LBA Data Size per format.
+    2. Use the collected information and iterate through each supported
+       format:-
+           - Create namespace.
+           - Attach namespace.
+           - Run IOs on the namespace under test.
+           - Detach namespace
+           - Delete Namespace.
+"""
+
+import time
+import subprocess
+from nose.tools import assert_equal
+from nvme_test import TestNVMe
+
+
+class TestNVMeFormatCmd(TestNVMe):
+
+    """
+    Represents Format testcase.
+
+        - Attributes:
+              - dps : data protection information.
+              - flabs : LBA format information.
+              - 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.
+              - test_log_dir : directory for logs, temp files.
+    """
+
+    def __init__(self):
+        """ Pre Section for TestNVMeFormatCmd """
+        TestNVMe.__init__(self)
+        self.dps = 0                 # ns data protection settings
+        self.flbas = 0               # ns formattes logical block settings
+        self.nsze = 0x1400000        # ns size
+        self.ncap = 0x1400000        # ns capacity
+        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 __del__(self):
+        """
+        Post Section for TestNVMeFormatCmd
+
+            - Create primary namespace.
+            - Atttach it to controller.
+            - Call super class's destructor.
+        """
+        assert_equal(self.create_and_validate_ns(self.default_nsid,
+                                                 self.nsze,
+                                                 self.ncap,
+                                                 self.flbas,
+                                                 self.dps), 0)
+        self.attach_ns(self.ctrl_id, self.default_nsid)
+        TestNVMe.__del__(self)
+
+    def attach_detach_primary_ns(self):
+        """ Extract supported format information using default namespace """
+        assert_equal(self.create_and_validate_ns(self.default_nsid,
+                                                 self.nsze,
+                                                 self.ncap,
+                                                 self.flbas,
+                                                 self.dps), 0)
+        assert_equal(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)
+        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)
+            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)
+            self.ms_list = proc.stdout.read().strip().split(" ")
+            assert_equal(self.detach_ns(self.ctrl_id, self.default_nsid), 0)
+            assert_equal(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()
+
+        # 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
+            err = self.create_and_validate_ns(self.default_nsid,
+                                              self.nsze,
+                                              self.ncap,
+                                              self.lba_format_list[i],
+                                              metadata_size)
+            assert_equal(err, 0)
+            assert_equal(self.attach_ns(self.ctrl_id, self.default_nsid), 0)
+            self.run_ns_io(self.default_nsid, self.lbads_list[i])
+            time.sleep(5)
+            assert_equal(self.detach_ns(self.ctrl_id, self.default_nsid), 0)
+            assert_equal(self.delete_and_validate_ns(self.default_nsid), 0)
+            self.nvme_reset_ctrl()
diff --git a/tests/nvme_get_features_test.py b/tests/nvme_get_features_test.py
new file mode 100644
index 0000000..7511de9
--- /dev/null
+++ b/tests/nvme_get_features_test.py
@@ -0,0 +1,103 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+Get Features Testcase:-
+
+Test the Mandetory features with get features command:-
+    1. 01h rbitration.
+    2. 02h M Power Management.
+    3. 04h M Temperature Threshold.
+    4. 05h M Error Recovery.
+    5. 07h M Number of Queues.
+    6. 08h M Interrupt Coalescing.
+    7. 09h M Interrupt Vector Configuration.
+    8. 0Ah M Write Atomicity Normal.
+    9. 0Bh M Asynchronous Event Configuration.
+"""
+
+import subprocess
+from nose.tools import assert_equal
+from nvme_test import TestNVMe
+
+
+class TestNVMeGetMandetoryFeatures(TestNVMe):
+
+    """
+    Represents Get Features testcase.
+
+        - Attributes:
+              - feature_id_list : list of the mandetory features.
+              - get_vector_list_cmd : vector list collection for 09h.
+              - vector_list : vector list to hold the interrupt vectors.
+    """
+
+    def __init__(self):
+        """ Pre Section for TestNVMeGetMandetoryFeatures """
+        TestNVMe.__init__(self)
+        self.setup_log_dir(self.__class__.__name__)
+        self.feature_id_list = ["0x01", "0x02", "0x04", "0x05", "0x07",
+                                "0x08", "0x09", "0x0A", "0x0B"]
+        get_vector_list_cmd = "cat /proc/interrupts | grep nvme |" \
+                              " cut -d : -f 1 | tr -d ' ' | tr '\n' ' '"
+        proc = subprocess.Popen(get_vector_list_cmd,
+                                shell=True,
+                                stdout=subprocess.PIPE)
+        self.vector_list = []
+        self.vector_list = proc.stdout.read().strip().split(" ")
+
+    def __del__(self):
+        """ Post Section for TestNVMeGetMandetoryFeatures
+
+            Call super class's destructor.
+        """
+        TestNVMe.__del__(self)
+
+    def get_mandetory_features(self, feature_id):
+        """ Wrapper for NVMe get features command
+            - Args:
+                - feature_id : feature id to be used with get feature command.
+            - Returns:
+                - None
+        """
+        if str(feature_id) == "0x09":
+            for vector in self.vector_list:
+                get_feat_cmd = "nvme get-feature " + self.ctrl + \
+                               " --feature-id=" + str(feature_id) + \
+                               " --cdw11=" + str(vector)
+                proc = subprocess.Popen(get_feat_cmd,
+                                        shell=True,
+                                        stdout=subprocess.PIPE)
+                feature_output = proc.communicate()[0]
+                print feature_output
+                assert_equal(proc.wait(), 0)
+        else:
+            get_feat_cmd = "nvme get-feature " + self.ctrl + \
+                           " --feature-id=" + str(feature_id)
+            proc = subprocess.Popen(get_feat_cmd,
+                                    shell=True,
+                                    stdout=subprocess.PIPE)
+            feature_output = proc.communicate()[0]
+            print feature_output
+            assert_equal(proc.wait(), 0)
+
+    def test_get_mandetory_features(self):
+        """ Testcase main """
+        for feature_id in self.feature_id_list:
+            self.get_mandetory_features(feature_id)
diff --git a/tests/nvme_read_write_test.py b/tests/nvme_read_write_test.py
new file mode 100644
index 0000000..a5f4579
--- /dev/null
+++ b/tests/nvme_read_write_test.py
@@ -0,0 +1,72 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+NVMe Read/Write Testcae:-
+
+    1. Create data file with specific pattern outside of the device under test.
+    2. Write data file on the namespace under test.
+    3. Read the data from the namespace under test into different file.
+    4. Compare file in #1 and #3.
+"""
+
+import filecmp
+from nose.tools import assert_equal
+from nvme_test_io import TestNVMeIO
+
+
+class TestNVMeReadWriteTest(TestNVMeIO):
+
+    """
+    Represents NVMe read, write testcase.
+
+        - Attributes:
+              - start_block : starting block of to perform IO.
+              - compare_file : data file to use in nvme comapre commmand.
+              - test_log_dir : directory for logs, temp files.
+    """
+    def __init__(self):
+        """ Pre Section for TestNVMeReadWriteTest """
+        TestNVMeIO.__init__(self)
+        self.start_block = 1023
+        self.test_log_dir = self.log_dir + "/" + self.__class__.__name__
+        self.setup_log_dir(self.__class__.__name__)
+        self.write_file = self.test_log_dir + "/" + self.write_file
+        self.read_file = self.test_log_dir + "/" + self.read_file
+        self.create_data_file(self.write_file, self.data_size, "15")
+        open(self.read_file, 'a').close()
+
+    def __del__(self):
+        """ Post Section for TestNVMeReadWriteTest """
+        TestNVMeIO.__del__(self)
+
+    def read_validate(self):
+        """ Validate the data file read
+            - Args:
+                - None
+            - Returns:
+                - returns 0 on success, 1 on failure.
+        """
+        return 0 if filecmp.cmp(self.read_file, self.write_file) else 1
+
+    def test_nvme_write(self):
+        """ Testcaes main  """
+        assert_equal(self.nvme_write(), 0)
+        assert_equal(self.nvme_read(), 0)
+        assert_equal(self.read_validate(), 0)
diff --git a/tests/nvme_simple_template_test.py b/tests/nvme_simple_template_test.py
new file mode 100644
index 0000000..b6736da
--- /dev/null
+++ b/tests/nvme_simple_template_test.py
@@ -0,0 +1,55 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+""" Simple Template test example :-
+"""
+
+from nvme_test import TestNVMe
+
+
+class TestNVMeSimpleTestTemplate(TestNVMe):
+
+    """ Represents Simple NVMe test """
+
+    def __init__(self):
+        """ Pre Section for TestNVMeSimpleTestTemplate. """
+        TestNVMe.__init__(self)
+        self.setup_log_dir(self.__class__.__name__)
+        # Add this test specific variables here
+
+    def __del__(self):
+        """ Post Section for TestNVMeSimpleTestTemplate
+
+            Call super class's destructor.
+        """
+        # Add this test specific cleanup code here
+        TestNVMe.__del__(self)
+
+    def simple_template_test(self):
+        """ Wrapper for this test specific functions
+            - Args:
+                - None
+            - Returns:
+                - None
+        """
+        pass
+
+    def test_get_mandetory_features(self):
+        """ Testcase main """
+        self.simple_template_test()
diff --git a/tests/nvme_smart_log_test.py b/tests/nvme_smart_log_test.py
new file mode 100644
index 0000000..e1eb6e5
--- /dev/null
+++ b/tests/nvme_smart_log_test.py
@@ -0,0 +1,86 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+NVMe Smart Log Verification Testcase:-
+
+    1. Execute smat-log on controller.
+    2. Execute smart-log on each available namespace.
+
+"""
+
+from nose.tools import assert_equal
+from nvme_test import TestNVMe
+
+
+class TestNVMeSmartLogCmd(TestNVMe):
+
+    """
+    Represents Smart Log testcae.
+
+        - Attributes:
+    """
+
+    def __init__(self):
+        """ Pre Section for TestNVMeSmartLogCmd """
+        TestNVMe.__init__(self)
+        self.setup_log_dir(self.__class__.__name__)
+
+    def __del__(self):
+        """
+        Post Section for TestNVMeSmartLogCmd
+
+            - Call super class's destructor.
+        """
+        TestNVMe.__del__(self)
+
+    def get_smart_log_ctrl(self):
+        """ Wrapper for executing smart-log on controller.
+            - Args:
+                - None:
+            - Returns:
+                - 0 on success, error code on failure.
+        """
+        return self.get_smart_log("0xFFFFFFFF")
+
+    def get_smart_log_ns(self, nsid):
+        """ Wrapper for executing smart-log on a namespace.
+            - Args:
+                - nsid: namespace id to be used in smart-log command.
+            - Returns:
+                - 0 on success, error code on failure.
+        """
+        return self.get_smart_log(nsid)
+
+    def get_smart_log_all_ns(self):
+        """ Wrapper for executing smart-log on all the namespaces.
+            - Args:
+                - None:
+            - 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])
+        return 0
+
+    def test_smart_log(self):
+        """ Testcase main """
+        assert_equal(self.get_smart_log_ctrl(), 0)
+        assert_equal(self.get_smart_log_all_ns(), 0)
diff --git a/tests/nvme_test.py b/tests/nvme_test.py
new file mode 100644
index 0000000..c3ee16e
--- /dev/null
+++ b/tests/nvme_test.py
@@ -0,0 +1,395 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+""" Base class for all the testcases
+"""
+
+import re
+import os
+import sys
+import json
+import mmap
+import stat
+import time
+import shutil
+import string
+import subprocess
+from nose import tools
+from nose.tools import assert_equal
+from nvme_test_logger import TestNVMeLogger
+
+
+class TestNVMe(object):
+
+    """
+    Represents a testcase, each testcase shuold inherit this
+    class or appropriate subclass which is a child of this class.
+
+    Common utility functions used in various testcases.
+
+        - Attributes:
+            - ctrl : NVMe Controller.
+            - ns1 : default namespace.
+            - default_nsid : default namespace id.
+            - config_file : configuration file.
+            - clear_log_dir : default log directory.
+    """
+
+    def __init__(self):
+        """ Pre Section for TestNVMe. """
+        # common code used in various testcases.
+        self.ctrl = "XXX"
+        self.ns1 = "XXX"
+        self.test_log_dir = "XXX"
+        self.default_nsid = 0x1
+        self.config_file = 'config.json'
+
+        self.load_config()
+        self.validate_pci_device()
+
+    def __del__(self):
+        """ Post Section for TestNVMe. """
+        if self.clear_log_dir is True:
+            shutil.rmtree(self.log_dir, ignore_errors=True)
+
+    @tools.nottest
+    def validate_pci_device(self):
+        """ Validate underlaying device belogs to pci subsystem.
+            - Args:
+                - None
+            - Returns:
+                - None
+        """
+        cmd = cmd = "find /sys/devices -name \\*nvme0 | grep -i pci"
+        err = subprocess.call(cmd, shell=True)
+        assert_equal(err, 0, "ERROR : Only NVMe PCI subsystem is supported")
+
+    @tools.nottest
+    def load_config(self):
+        """ Load Basic test configuration.
+            - Args:
+                - None
+            - Returns:
+                - None
+        """
+        with open(self.config_file) as data_file:
+            config = json.load(data_file)
+            self.ctrl = config['controller']
+            self.ns1 = config['ns1']
+            self.log_dir = config['log_dir']
+            self.clear_log_dir = False
+
+            if self.clear_log_dir is True:
+                shutil.rmtree(self.log_dir, ignore_errors=True)
+
+            if not os.path.exists(self.log_dir):
+                os.makedirs(self.log_dir)
+
+    @tools.nottest
+    def setup_log_dir(self, test_name):
+        """ Set up the log directory for a testcase
+            Args:
+              - test_name : name of the testcase.
+            Returns:
+              - None
+        """
+        self.test_log_dir = self.log_dir + "/" + test_name
+        if not os.path.exists(self.test_log_dir):
+            os.makedirs(self.test_log_dir)
+        sys.stdout = TestNVMeLogger(self.test_log_dir + "/" + "stdout.log")
+        sys.stderr = TestNVMeLogger(self.test_log_dir + "/" + "stderr.log")
+
+    @tools.nottest
+    def exec_cmd(self, cmd):
+        """ Wrapper for executing a shell command and return the result. """
+        proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
+        return proc.wait()
+
+    @tools.nottest
+    def nvme_reset_ctrl(self):
+        """ Wrapper for nvme reset command.
+            - Args:
+                - None:
+            - Returns:
+                - None
+        """
+        nvme_reset_cmd = "nvme reset " + self.ctrl
+        err = subprocess.call(nvme_reset_cmd,
+                              shell=True,
+                              stdout=subprocess.PIPE)
+        assert_equal(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)
+        time.sleep(5)
+        assert_equal(proc.wait(), 0, "ERROR : pci rescan failed")
+
+    @tools.nottest
+    def get_ctrl_id(self):
+        """ Wrapper for extracting the controller id.
+            - Args:
+                - None
+            - Returns:
+                - controller id.
+        """
+        get_ctrl_id = "nvme list-ctrl " + self.ctrl
+        proc = subprocess.Popen(get_ctrl_id,
+                                shell=True,
+                                stdout=subprocess.PIPE)
+        err = proc.wait()
+        assert_equal(err, 0, "ERROR : nvme list-ctrl failed")
+        line = proc.stdout.readline()
+        ctrl_id = line.split(":")[1].strip()
+        return ctrl_id
+
+    @tools.nottest
+    def get_ns_list(self):
+        """ Wrapper for extrating the namespace list.
+            - Args:
+                - None
+            - Returns:
+                - List of the namespaces.
+        """
+        ns_list = []
+        ns_list_cmd = "nvme list-ns " + self.ctrl
+        proc = subprocess.Popen(ns_list_cmd,
+                                shell=True,
+                                stdout=subprocess.PIPE)
+        assert_equal(proc.wait(), 0, "ERROR : nvme list namespace failed")
+        for line in proc.stdout:
+            ns_list.append(string.replace(line.split(":")[1], '\n', ''))
+
+        return ns_list
+
+    @tools.nottest
+    def get_max_ns(self):
+        """ Wrapper for extracting maximum number of namspaces supported.
+            - Args:
+                - None
+            - 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
+        proc = subprocess.Popen(max_ns_cmd,
+                                shell=True,
+                                stdout=subprocess.PIPE)
+        err = proc.wait()
+        assert_equal(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)
+
+    @tools.nottest
+    def delete_all_ns(self):
+        """ Wrapper for deleting all the namespaces.
+            - Args:
+                - None
+            - Returns:
+                - None
+        """
+        delete_ns_cmd = "nvme delete-ns " + self.ctrl + " -n 0xFFFFFFFF"
+        assert_equal(self.exec_cmd(delete_ns_cmd), 0)
+        list_ns_cmd = "nvme list-ns " + self.ctrl + " --all | wc -l"
+        proc = subprocess.Popen(list_ns_cmd,
+                                shell=True,
+                                stdout=subprocess.PIPE)
+        output = proc.stdout.read().strip()
+        assert_equal(output, '0', "ERROR : deleting all namespace failed")
+
+    @tools.nottest
+    def create_ns(self, nsze, ncap, flbas, dps):
+        """ Wrapper for creating a namespace.
+            - Args:
+                - nsze : new namespace size.
+                - ncap : new namespace capacity.
+                - flbas : new namespace format.
+                - dps : new namespace data protection information.
+            - 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)
+        return self.exec_cmd(create_ns_cmd)
+
+    @tools.nottest
+    def create_and_validate_ns(self, nsid, nsze, ncap, flbas, dps):
+        """ Wrapper for creating and validating a namespace.
+            - Args:
+                - nsid : new namespace id.
+                - nsze : new namespace size.
+                - ncap : new namespace capacity.
+                - flbas : new namespace format.
+                - dps : new namespace data protection information.
+            - Returns:
+                - return 0 on success, error code on failure.
+        """
+        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)
+            err = subprocess.call(id_ns_cmd,
+                                  shell=True,
+                                  stdout=subprocess.PIPE)
+        return err
+
+    @tools.nottest
+    def attach_ns(self, ctrl_id, ns_id):
+        """ Wrapper for attaching the namespace.
+            - Args:
+                - ctrl_id : controller id to which namespace to be attched.
+                - nsid : new namespace 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
+        err = subprocess.call(attach_ns_cmd,
+                              shell=True,
+                              stdout=subprocess.PIPE)
+        time.sleep(5)
+        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
+
+    @tools.nottest
+    def detach_ns(self, ctrl_id, nsid):
+        """ Wrapper for detaching the namespace.
+            - Args:
+                - ctrl_id : controller id to which namespace to be attched.
+                - nsid : new namespace id.
+            - Returns:
+                - 0 on success, error code on failure.
+        """
+        detach_ns_cmd = "nvme detach-ns " + self.ctrl + \
+                        " --namespace-id=" + str(nsid) + \
+                        " --controllers=" + ctrl_id
+        return subprocess.call(detach_ns_cmd,
+                               shell=True,
+                               stdout=subprocess.PIPE)
+
+    @tools.nottest
+    def delete_and_validate_ns(self, nsid):
+        """ Wrapper for deleting and validating that namespace is deleted.
+            - Args:
+                - nsid : new namespace id.
+            - Returns:
+                - 0 on success, 1 on failure.
+        """
+        # delete the namespace
+        delete_ns_cmd = "nvme delete-ns " + self.ctrl + " -n " + str(nsid)
+        err = subprocess.call(delete_ns_cmd,
+                              shell=True,
+                              stdout=subprocess.PIPE)
+        assert_equal(err, 0, "ERROR : delete namespace failed")
+        return err
+
+    def get_smart_log(self, nsid):
+        """ Wrapper for nvme smart-log command.
+            - Args:
+                - nsid : namespace id to get smart log from.
+            - Returns:
+                - 0 on success, error code on failure.
+        """
+        smart_log_cmd = "nvme smart-log " + self.ctrl + " -n " + str(nsid)
+        print smart_log_cmd
+        proc = subprocess.Popen(smart_log_cmd,
+                                shell=True,
+                                stdout=subprocess.PIPE)
+        err = proc.wait()
+        assert_equal(err, 0, "ERROR : nvme smart log failed")
+
+        for line in proc.stdout:
+            if "data_units_read" in line:
+                data_units_read = \
+                    string.replace(line.split(":")[1].strip(), ",", "")
+            if "data_units_written" in line:
+                data_units_written = \
+                    string.replace(line.split(":")[1].strip(), ",", "")
+            if "host_read_commands" in line:
+                host_read_commands = \
+                    string.replace(line.split(":")[1].strip(), ",", "")
+            if "host_write_commands" in line:
+                host_write_commands = \
+                    string.replace(line.split(":")[1].strip(), ",", "")
+
+        print "data_units_read " + data_units_read
+        print "data_units_written " + data_units_written
+        print "host_read_commands " + host_read_commands
+        print "host_write_commands " + host_write_commands
+        return err
+
+    def get_error_log(self, nsid):
+        """ Wrapper for nvme error-log command.
+            - Args:
+                - nsid : namespace id to get error log from.
+            - Returns:
+                - 0 on success, error code on failure.
+        """
+        pattern = re.compile("^ Entry\[[ ]*[0-9]+\]")
+        error_log_cmd = "nvme error-log " + self.ctrl + " -n " + str(nsid)
+        proc = subprocess.Popen(error_log_cmd,
+                                shell=True,
+                                stdout=subprocess.PIPE)
+        err = proc.wait()
+        assert_equal(err, 0, "ERROR : nvme error log failed")
+        line = proc.stdout.readline()
+        err_log_entry_count = int(line.split(" ")[5].strip().split(":")[1])
+        entry_count = 0
+        for line in proc.stdout:
+            if pattern.match(line):
+                entry_count += 1
+
+        return 0 if err_log_entry_count == entry_count else 1
+
+    def run_ns_io(self, nsid, lbads):
+        """ Wrapper to run ios on namespace under test.
+            - Args:
+                - lbads : LBA Data size supported in power of 2 format.
+            - Returns:
+                - None
+        """
+        block_size = mmap.PAGESIZE if 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=10 > /dev/null 2>&1"
+        print io_cmd
+        run_io = subprocess.Popen(io_cmd, shell=True, stdout=subprocess.PIPE)
+        run_io_result = run_io.communicate()[1]
+        assert_equal(run_io_result, None)
+        io_cmd = "dd if=/dev/zero of=" + ns_path + " bs=" + \
+                 str(block_size) + " count=10 > /dev/null 2>&1"
+        print io_cmd
+        run_io = subprocess.Popen(io_cmd, shell=True, stdout=subprocess.PIPE)
+        run_io_result = run_io.communicate()[1]
+        assert_equal(run_io_result, None)
diff --git a/tests/nvme_test_io.py b/tests/nvme_test_io.py
new file mode 100644
index 0000000..e2a6b46
--- /dev/null
+++ b/tests/nvme_test_io.py
@@ -0,0 +1,99 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+""" Inherit TestNVMeIO for nvme read/write operations """
+
+import os
+from nose import tools
+from nvme_test import TestNVMe
+
+
+class TestNVMeIO(TestNVMe):
+
+    """
+    Variable and Methods required to perform nvme read/write.
+
+        - Attributes:
+              - data_size : data size to perform IO.
+              - start_block : starting block of to perform IO.
+              - block_count : Number of blocks to use in IO.
+              - write_file : data file to use in nvme write command.
+              - read_file : data file to use in nvme read command.
+    """
+
+    def __init__(self):
+        """ Pre Section for TestNVMeIO """
+        TestNVMe.__init__(self)
+        # common code used in various testcases.
+        self.data_size = 512
+        self.start_block = 0
+        self.block_count = 0
+        self.write_file = "write_file.txt"
+        self.read_file = "read_file.txt"
+
+    def __del__(self):
+        """ Post Section for TestNVMeIO """
+        TestNVMe.__del__(self)
+
+    @tools.nottest
+    def create_data_file(self, pathname, data_size, pattern):
+        """ Creates data file with specific pattern
+            - Args:
+                - pathname : data file path name.
+                - data_size : total size of the data.
+                - pattern : data pattern to create file.
+            - Returns:
+            None
+        """
+        pattern_len = len(pattern)
+        data_file = open(pathname, "w")
+        for i in range(0, data_size):
+            data_file.write(pattern[i % pattern_len])
+        data_file.flush()
+        os.fsync(data_file.fileno())
+        data_file.close()
+
+    @tools.nottest
+    def nvme_write(self):
+        """ Wrapper for nvme write operation
+            - Args:
+                - None
+            - 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
+        return self.exec_cmd(write_cmd)
+
+    @tools.nottest
+    def nvme_read(self):
+        """ Wrapper for nvme read operation
+            - Args:
+                - None
+            - 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
+        return self.exec_cmd(read_cmd)
diff --git a/tests/nvme_test_logger.py b/tests/nvme_test_logger.py
new file mode 100644
index 0000000..c59fa18
--- /dev/null
+++ b/tests/nvme_test_logger.py
@@ -0,0 +1,52 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+Logger for NVMe Test Framwwork:-
+
+"""
+import sys
+
+
+class TestNVMeLogger(object):
+    """ Represents Logger for NVMe Testframework.  """
+    def __init__(self, log_file_path):
+        """ Logger setup
+            - Args:
+                log_file_path : path to store the log.
+        """
+        self.terminal = sys.stdout
+        self.log = open(log_file_path, "w")
+
+    def write(self, log_message):
+        """ Logger setup
+            - Args:
+                log_message: string to write in the log file.
+            - Returns:
+                None
+        """
+        self.terminal.write(log_message)
+        self.log.write(log_message)
+
+    def flush(self):
+        """ This flush method is needed for python 3 compatibility.
+            this handles the flush command by doing nothing.
+            you might want to specify some extra behavior here.
+        """
+        pass
diff --git a/tests/nvme_writeuncor_test.py b/tests/nvme_writeuncor_test.py
new file mode 100644
index 0000000..9f96f63
--- /dev/null
+++ b/tests/nvme_writeuncor_test.py
@@ -0,0 +1,76 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+NVMe Write Compare Testcae:-
+
+    1. Read block of data successfully.
+    2. Issue write uncorrectable to block of data.
+    3. Attempt to read from same block; shall fail.
+    4. Issue a write command to first block of data.
+    5. Read from the same block; shall pass.
+
+"""
+
+from nose.tools import assert_equal, assert_not_equal
+from nvme_test_io import TestNVMeIO
+
+
+class TestNVMeUncor(TestNVMeIO):
+
+    """
+    Represents NVMe Write Uncorrecatble testcase.
+        - Attributes:
+              - start_block : starting block of to perform IO.
+              - test_log_dir : directory for logs, temp files.
+    """
+
+    def __init__(self):
+        """ Constructor TestNVMeUncor """
+        TestNVMeIO.__init__(self)
+        self.start_block = 1023
+        self.setup_log_dir(self.__class__.__name__)
+        self.write_file = self.test_log_dir + "/" + self.write_file
+        self.read_file = self.test_log_dir + "/" + self.read_file
+        self.create_data_file(self.write_file, self.data_size, "15")
+        open(self.read_file, 'a').close()
+
+    def __del__(self):
+        """ Post Section for TestNVMeUncor """
+        TestNVMeIO.__del__(self)
+
+    def write_uncor(self):
+        """ Wrapper for nvme write uncorrectable
+            - Args:
+                - None
+            - 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)
+        return self.exec_cmd(write_uncor_cmd)
+
+    def test_write_uncor(self):
+        """ Testcase main """
+        assert_equal(self.nvme_read(), 0)
+        assert_equal(self.write_uncor(), 0)
+        assert_not_equal(self.nvme_read(), 0)
+        assert_equal(self.nvme_write(), 0)
+        assert_equal(self.nvme_read(), 0)
diff --git a/tests/nvme_writezeros_test.py b/tests/nvme_writezeros_test.py
new file mode 100644
index 0000000..157fd78
--- /dev/null
+++ b/tests/nvme_writezeros_test.py
@@ -0,0 +1,102 @@
+# Copyright (c) 2015-2016 Western Digital Corporation or its affiliates.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.
+#
+#   Author: Chaitanya Kulkarni <chaitanya.kulkarni at hgst.com>
+#
+"""
+NVMe Write Zeros:-
+
+    1. Issue a write command to block of data.
+    2. Read from same block to verify data pattern.
+    3. Issue write zeros to the block of data.
+    4. Read from same block, should be all zeroes.
+
+"""
+
+import filecmp
+from nose.tools import assert_equal
+from nvme_test_io import TestNVMeIO
+
+
+class TestNVMeWriteZeros(TestNVMeIO):
+
+    """
+    Represents NVMe Write Zero Testcase.
+
+        - Attributes:
+              - zero_file : file with all '\0' to compare the zero data.
+              - data_size : data size to perform IO.
+              - start_block : starting block of to perform IO.
+              - block_count: Number of blocks to use in IO.
+              - test_log_dir : directory for logs, temp files.
+    """
+    def __init__(self):
+        """ Pre Section for TestNVMeWriteZeros """
+        TestNVMeIO.__init__(self)
+        self.start_block = 1023
+        self.block_count = 0
+        self.setup_log_dir(self.__class__.__name__)
+        self.write_file = self.test_log_dir + "/" + self.write_file
+        self.read_file = self.test_log_dir + "/" + self.read_file
+        self.zero_file = self.test_log_dir + "/" + "zero_file.txt"
+        self.create_data_file(self.write_file, self.data_size, "15")
+        self.create_data_file(self.zero_file, self.data_size, '\0')
+        open(self.read_file, 'a').close()
+
+    def __del__(self):
+        """ Post Section for TestNVMeWriteZeros """
+        TestNVMeIO.__del__(self)
+
+    def write_zeroes(self):
+        """ Wrapper for nvme write-zeroe
+            - Args:
+                - None
+            - 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)
+        return self.exec_cmd(write_zeroes_cmd)
+
+    def validate_write_read(self):
+        """ Validate the file which had been read from the device
+            - Args:
+                - None
+            - Returns:
+                - 0 on success, 1 on failure
+        """
+        return 0 if filecmp.cmp(self.write_file, self.read_file) is True else 1
+
+    def validate_zeroes(self):
+        """
+        Validate the data which is zeroed out via write-zeroes
+            - Args:
+                - None
+            - Returns:
+                - 0 on success, 1 on failure
+         """
+        return 0 if filecmp.cmp(self.zero_file, self.read_file) is True else 1
+
+    def test_write_zeros(self):
+        """ Testcae main """
+        assert_equal(self.nvme_write(), 0)
+        assert_equal(self.nvme_read(), 0)
+        assert_equal(self.validate_write_read(), 0)
+        assert_equal(self.write_zeroes(), 0)
+        assert_equal(self.nvme_read(), 0)
+        assert_equal(self.validate_zeroes(), 0)
-- 
1.9.1




More information about the Linux-nvme mailing list