Examples

In this chapter, we will review several typical NVMe test scripts. You can find more example scripts here: https://github.com/pynvme/pynvme/blob/master/scripts/test_examples.py

Ex1: hello world

 1# import packages
 2import pytest
 3import nvme as d
 4
 5# define the test case in a python function
 6# list fixtures used in the parameter list
 7# specify the class type of the fixture, so VSCode can give more docstring online
 8def test_hello_world(nvme0, nvme0n1: d.Namespace):
 9    # create the buffers and fill data for read/write commands
10    read_buf = d.Buffer(512)
11    data_buf = d.Buffer(512)
12    data_buf[10:21] = b'hello world'
13
14    # create IO Qpair for read/write commands
15    qpair = d.Qpair(nvme0, 16)
16
17    # Define the callback function for write command.
18    # The argument *status1* of the callback is a 16-bit
19    # value, which includes the Phase-bit.
20    def write_cb(cdw0, status1):
21        nvme0n1.read(qpair, read_buf, 0, 1)
22
23    # execute the write command with the callback function
24    nvme0n1.write(qpair, data_buf, 0, 1, cb=write_cb)
25
26    # wait the write command, and the read command in its callback, to be completed
27    qpair.waitdone(2)
28
29    # check the data in read buffer
30    assert read_buf[10:21] == b'hello world'

Ex2: sanitize

 1# import more package for GUI programming
 2import PySimpleGUI as sg
 3
 4# define another test function, use the default buffer created by the fixture
 5def test_sanitize(nvme0, nvme0n1, buf):
 6    # check if sanitize is supported by the device
 7    if nvme0.id_data(331, 328) == 0:
 8        pytest.skip("sanitize operation is not supported")
 9
10    # start sanitize operation
11    logging.info("supported sanitize operation: %d" % nvme0.id_data(331, 328))
12    nvme0.sanitize().waitdone()
13
14    # polling sanitize status in its log page
15    nvme0.getlogpage(0x81, buf, 20).waitdone()
16    while buf.data(3, 2) & 0x7 != 1:  # sanitize is not completed
17        progress = buf.data(1, 0)*100//0xffff
18        # display the progress of sanitize in a GUI window
19        sg.OneLineProgressMeter('sanitize progress', progress, 100,
20                                'progress', orientation='h')
21        nvme0.getlogpage(0x81, buf, 20).waitdone()
22        time.sleep(1)

Ex3: parameterized tests

 1# create a parameter with a argument list
 2@pytest.mark.parametrize("qcount", [1, 2, 4, 8, 16])
 3def test_ioworker_iops_multiple_queue(nvme0n1, qcount):
 4    l = []
 5    io_total = 0
 6
 7    # create multiple ioworkers for read performance test
 8    for i in range(qcount):
 9        a = nvme0n1.ioworker(io_size=8, lba_align=8,
10                             region_start=0, region_end=256*1024*8, # 1GB space
11                             lba_random=False, qdepth=16,
12                             read_percentage=100, time=10).start()
13        l.append(a)
14
15    # after all ioworkers complete, calculate the IOPS performance result
16    for a in l:
17        r = a.close()
18        io_total += (r.io_count_read+r.io_count_nonread)
19    logging.info("Q %d IOPS: %dK" % (qcount, io_total/10000))

Ex4: upgrade and reboot the drive

 1# this test function is actually a utility to upgrade SSD firmware
 2def test_download_firmware(nvme0, subsystem):
 3    # open the firmware binary image file
 4    filename = sg.PopupGetFile('select the firmware binary file', 'pynvme')
 5    if filename:
 6        logging.info("To download firmware binary file: " + filename)
 7
 8        # download the firmware image to SSD
 9        nvme0.downfw(filename)
10
11        # power cycle the SSD to activate the upgraded firmware
12        subsystem.power_cycle()
13
14        # reset controller after power cycle
15        nvme0.reset()

Ex5: write drive and monitor temperature

 1# a temperature calculation package
 2from pytemperature import k2c
 3
 4def test_ioworker_with_temperature(nvme0, nvme0n1):
 5    smart_log = d.Buffer(512, "smart log")
 6
 7    # start the ioworker for sequential writing in secondary process
 8    with nvme0n1.ioworker(io_size=256, lba_align=256,
 9                          lba_random=False, qdepth=16,
10                          read_percentage=0, time=30):
11        # meanwhile, monitor SMART temperature in primary process
12        for i in range(40):
13            nvme0.getlogpage(0x02, smart_log, 512).waitdone()
14
15            # the K temperture from SMART log page
16            ktemp = smart_log.data(2, 1)
17            logging.info("temperature: %0.2f degreeC" % k2c(ktemp))
18            time.sleep(1)

Ex6: multiple ioworkers on different namespaces and controllers

 1def test_multiple_controllers_and_namespaces():
 2    # address list of the devices to test
 3    addr_list = ['3a:00.0', '10.24.48.17']
 4
 5    # create the list of controllers and namespaces
 6    nvme_list = [d.Controller(d.Pcie(a)) for a in addr_list]
 7    ns_list = [d.Namespace(n) for n in nvme_list]
 8
 9    # operations on multiple controllers
10    for nvme in nvme_list:
11        logging.info("device: %s" % nvme.id_data(63, 24, str))
12
13    # start multiple ioworkers
14    ioworkers = {}
15    for ns in ns_list:
16        a = ns.ioworker(io_size=8, lba_align=8,
17                        region_start=0, region_end=256*1024*8, # 1GB space
18                        lba_random=False, qdepth=16,
19                        read_percentage=100, time=10).start()
20        ioworkers[ns] = a
21
22    # test results of different namespaces
23    for ns in ioworkers:
24        r = ioworkers[ns].close()
25        io_total = (r.io_count_read+r.io_count_nonread)
26        logging.info("capacity: %u, IOPS: %.3fK" %
27                     (ns.id_data(7, 0), io_total/10000))

Ex7: format and fused operations

 1# fused operation is not directly supported by pynvme APIs
 2def test_fused_operations(nvme0, nvme0n1):
 3    # format the namespace to 4096 block size. Use Namespace.format(), instead
 4    # of Controller.format(), for pynvme to update namespace data in the driver.
 5    nvme0n1.format(4096)
 6
 7    # create qpair and buffer for IO commands
 8    q = d.Qpair(nvme0, 10)
 9    b = d.Buffer()
10
11    # separate compare and write commands
12    nvme0n1.write(q, b, 8).waitdone()
13    nvme0n1.compare(q, b, 8).waitdone()
14
15    # implement fused compare and write operations with generic commands
16    # Controller.send_cmd() sends admin commands,
17    # and Namespace.send_cmd() here sends IO commands.
18    nvme0n1.send_cmd(5|(1<<8), q, b, 1, 8, 0, 0)
19    nvme0n1.send_cmd(1|(1<<9), q, b, 1, 8, 0, 0)
20    q.waitdone(2)