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)