[PATCH V2 16/46] nvmftests-nvmf: add support for host subsystem
Chaitanya Kulkarni
ckulkarnilinux at gmail.com
Tue Oct 24 18:30:33 PDT 2017
From: Chaitanya Kulkarni <chaitanya.kulkarni at wdc.com>
This adds a new class to handle the host subsystem operations.
Signed-off-by: Chaitanya Kulkarni <chaitanya.kulkarni at wdc.com>
---
.../nvmftests/nvmf/host/host_subsystem.py | 527 +++++++++++++++++++++
1 file changed, 527 insertions(+)
create mode 100644 tools/testing/selftests/nvmftests/nvmf/host/host_subsystem.py
diff --git a/tools/testing/selftests/nvmftests/nvmf/host/host_subsystem.py b/tools/testing/selftests/nvmftests/nvmf/host/host_subsystem.py
new file mode 100644
index 0000000..e32d952
--- /dev/null
+++ b/tools/testing/selftests/nvmftests/nvmf/host/host_subsystem.py
@@ -0,0 +1,527 @@
+# Copyright (c) 2016-2017 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 wdc.com>
+#
+""" Represents NVMe Over Fabric Host Controller.
+"""
+
+import re
+import os
+import stat
+import time
+import random
+import string
+import subprocess
+from natsort import natsorted
+
+from utils.const import Const
+from utils.shell import Cmd
+from utils.log import Log
+from nvmf.host.host_ns import NVMFHostNamespace
+
+
+class NVMFHostController(object):
+ """
+ Represents a host controller.
+
+ - Attributes :
+ - nqn : ctrl nqn.
+ - ctrl_dev : controller device.
+ - ctrl_dict : controller attributes.
+ - ns_list : list of namespaces.
+ - ns_dev_list : namespace device list.
+ - transport : transport type.
+ """
+ def __init__(self, nqn, transport):
+ self.nqn = nqn
+ self.ctrl_dev = None
+ self.ctrl_dict = {}
+ self.ns_list = []
+ self.ns_dev_list = []
+ self.transport = transport
+ self.ns_list_index = 0
+ self.logger = Log.get_logger(__name__, 'host_subsystem')
+
+ def __iter__(self):
+ self.ns_list_index = 0
+ return self
+
+ def __next__(self):
+ index = self.ns_list_index
+ self.ns_list_index += 1
+ if (len(self.ns_list) > index):
+ return self.ns_list[index]
+ raise StopIteration
+
+ def next(self):
+ """ Iterator next function """
+ return self.__next__()
+
+ def run_io_all_ns(self, iocfg):
+ """ Start IOs on all the namespaces of this controller parallelly.
+ - Args :
+ - iocfg : io configuration.
+ - Returns :
+ - True on success, False on failure.
+ """
+ ret = True
+ for ns in iter(self):
+ try:
+ if ns.start_io(iocfg) is False:
+ self.logger.error("start IO " + ns.ns_dev + ".")
+ ret = False
+ break
+ self.logger.info("start IO " + ns.ns_dev + " SUCCESS.")
+ except StopIteration:
+ break
+
+ return ret
+
+ def wait_io_all_ns(self):
+ """ Wait until workqueue is empty.
+ - Args :
+ - None.
+ - Returns :
+ - True if namespace wait IO is successful, False otherwise.
+ """
+ for ns in iter(self):
+ try:
+ ret = ns.wait_io()
+ if ret is False:
+ return False
+ except StopIteration:
+ break
+ return True
+
+ def run_io_seq(self, iocfg):
+ """ Exercise IOs on each namespace.
+ - Args :
+ - iocfg : io configuration.
+ - Returns :
+ - True on success, False on failure.
+ """
+ ret = True
+ for ns in iter(self):
+ try:
+ if ns.start_io(iocfg) is False:
+ self.logger.error("start IO " + ns.ns_dev + ".")
+ ret = False
+ break
+ ret = ns.wait_io()
+ except StopIteration:
+ break
+ return ret
+
+ def run_mkfs_seq(self, fs_type):
+ """ Run mkfs.
+ - Args :
+ - fs_type : file system type.
+ - Returns :
+ - True on success, False on failure.
+ """
+ ret = True
+ for ns in iter(self):
+ try:
+ if ns.mkfs(fs_type) is False:
+ ret = False
+ break
+ except StopIteration:
+ break
+ return ret
+
+ def run_fs_ios(self, iocfg):
+ """ Run IOs on mounted file system.
+ - Args :
+ - iocfg : io configuration.
+ - Returns :
+ - True on success, False on failure.
+ """
+ ret = True
+ for ns in iter(self):
+ try:
+ if ns.run_fs_ios(iocfg) is False:
+ ret = False
+ break
+ except StopIteration:
+ break
+ return ret
+
+ def run_io_random(self, iocfg):
+ """ Select the namespce randomely and wait for the IOs completion,
+ repeat this for all the namespaces.
+ - Args :
+ - iocfg : io configuration.
+ - Returns :
+ - True on success, False on failure.
+ """
+ num_list = range(0, len(self.ns_list))
+
+ for i in range(0, len(self.ns_list)):
+ random.shuffle(num_list)
+ ns_id = num_list.pop()
+
+ host_ns = self.ns_list[ns_id]
+ if host_ns.start_io(iocfg) is False:
+ return False
+ host_ns.wait_io()
+
+ return True
+
+ def __ctrl_set_attr__(self, attr):
+ """ Set host controller attribute.
+ - Args :
+ - attr : sysfs attribute to set.
+ - Returns :
+ - True on success, False on failure.
+ """
+ sysfs_path = "/sys/class/nvme-fabrics/ctl/"
+ cmd = "echo 1 >" + sysfs_path + self.ctrl_dev.split('/')[-1] \
+ + "/" + attr
+ return Cmd.exec_cmd(cmd)
+
+ def ctrl_rescan(self):
+ """ Issue controller rescan.
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ return self.__ctrl_set_attr__("rescan_controller")
+
+ def ctrl_reset(self):
+ """ Issue controller reset.
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ return self.__ctrl_set_attr__("reset_controller")
+
+ def run_smart_log(self, nsid="0xFFFFFFFF"):
+ """ Wrapper for nvme smart-log command.
+ - Args :
+ - nsid : namespace id for smart log, defaults to ctrl.
+ - Returns:
+ - True on success, False on failure.
+ """
+ smart_log_cmd = "nvme smart-log " + self.ctrl_dev + " -n " + str(nsid)
+ self.logger.info(smart_log_cmd)
+ proc = subprocess.Popen(smart_log_cmd,
+ shell=True,
+ stdout=subprocess.PIPE)
+ err = proc.wait()
+ if err != 0:
+ self.logger.error("nvme smart log failed.")
+ return False
+
+ for line in proc.stdout:
+ if "data_units_read" in line:
+ temp_str = line.split(":")[Const.SMART_LOG_VALUE].strip()
+ data_units_read = string.replace(temp_str, ",", "")
+ if "data_units_written" in line:
+ temp_str = line.split(":")[Const.SMART_LOG_VALUE].strip()
+ data_units_written = string.replace(temp_str, ",", "")
+ if "host_read_commands" in line:
+ temp_str = line.split(":")[Const.SMART_LOG_VALUE].strip()
+ host_read_commands = string.replace(temp_str, ",", "")
+ if "host_write_commands" in line:
+ temp_str = line.split(":")[Const.SMART_LOG_VALUE].strip()
+ host_write_commands = string.replace(temp_str, ",", "")
+
+ self.logger.info("data_units_read " + data_units_read)
+ self.logger.info("data_units_written " + data_units_written)
+ self.logger.info("host_read_commands " + host_read_commands)
+ self.logger.info("host_write_commands " + host_write_commands)
+ return True
+
+ def smart_log(self):
+ """ Execute smart-log command.
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ # Run smart log on controller
+ self.run_smart_log()
+ i = 1
+ for ns in iter(self):
+ try:
+ if self.run_smart_log(i) is False:
+ return False
+ i += 1
+ except StopIteration:
+ break
+ return True
+
+ def validate_sysfs_ns(self):
+ """ Validate sysfs entries for the host controller and namespace(s).
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ ctrl_bdev = self.ctrl_dev.split("/")[Const.CTRL_BLK_FILE_NAME]
+ # Validate ctrl in the sysfs
+ cmd = "basename $(dirname $(grep -ls " + self.nqn + \
+ " /sys/class/nvme-fabrics/ctl/*/subsysnqn))"
+ proc = subprocess.Popen(cmd,
+ shell=True,
+ stdout=subprocess.PIPE)
+ for line in proc.stdout:
+ line = line.strip('\n')
+ # compare nvmeN in /dev/nvmeN in sysfs
+ if line != ctrl_bdev:
+ self.logger.error("host ctrl " + self.ctrl_dev +
+ " not present.")
+ return False
+ dir_list = os.listdir("/sys/class/nvme-fabrics/ctl/" + ctrl_bdev + "/")
+
+ pat = re.compile("^" + ctrl_bdev + "+n[0-9]+$")
+ for line in dir_list:
+ line = line.strip('\n')
+ if pat.match(line):
+ if "/dev/" + line not in self.ns_dev_list:
+ self.logger.error("ns " + line + " not found in sysfs.")
+ return False
+
+ self.logger.info("sysfs entries for ctrl and ns created successfully.")
+ return True
+
+ def build_ns_list(self):
+ """ Generate next available controller and namespace id on the fly.
+ Build the ns list for this controller.
+ - Args :
+ - None.
+ - Returns :
+ - ctrl and ns list on success, None on failure.
+ """
+ ctrl = Const.XXX
+ ns_list = []
+ try:
+ dev_list = os.listdir("/dev/")
+ except Exception, err:
+ self.logger.error(str(err) + ".")
+ return None, None
+ dev_list = natsorted(dev_list, key=lambda y: y.lower())
+ # we assume that atleast one namespace is created on target
+ # find ctrl
+ pat = re.compile("^nvme[0-9]+$")
+ for line in dev_list:
+ line = line.strip('\n')
+ if pat.match(line):
+ ctrl = line
+
+ if ctrl == Const.XXX:
+ self.logger.error("controller '/dev/nvme*' not found.")
+ return None, None
+ # allow namespaces to appear in the /dev/
+ time.sleep(2)
+ # find namespace(s) associated with ctrl
+ try:
+ dir_list = os.listdir("/dev/")
+ except Exception, err:
+ self.logger.error(str(err))
+ return None, None
+ pat = re.compile("^" + ctrl + "+n[0-9]+$")
+ for line in dir_list:
+ line = line.strip('\n')
+ if pat.match(line):
+ self.logger.info("Generated namespace name /dev/" + line + ".")
+ ns_list.append("/dev/" + line)
+
+ if len(ns_list) == 0:
+ self.logger.error("host ns not found for ctrl " + ctrl + ".")
+ return None, None
+
+ ctrl = "/dev/" + ctrl
+ return ctrl, ns_list
+
+ def init_ns(self):
+ """ Initialize and build namespace list and validate sysfs entries.
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ self.logger.info("Expecting following namespaces " +
+ str(self.ns_dev_list) + ".")
+ for ns_dev in self.ns_dev_list:
+ if not stat.S_ISBLK(os.stat(ns_dev).st_mode):
+ self.logger.error("expected block dev " + ns_dev + ".")
+ return False
+
+ self.logger.info("Found NS " + ns_dev + ".")
+ host_ns = NVMFHostNamespace(ns_dev)
+ host_ns.init()
+ self.ns_list.append(host_ns)
+ # allow sysfs entries to populate
+ time.sleep(1)
+ if self.validate_sysfs_ns() is False:
+ self.logger.error("unable to verify sysfs entries.")
+ return False
+ self.logger.info("Host sysfs entries are validated.")
+ return True
+
+ def validate_fabric_ctrl(self):
+ """ Validate this is a fabric controller.
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+
+ if not stat.S_ISCHR(os.stat(self.ctrl_dev).st_mode):
+ self.logger.error("failed to find char device for host ctrl.")
+
+ cmd = "find /sys/devices -name " + self.ctrl_dev.split("/")[-2] \
+ + " | grep nvme-fabric"
+ self.logger.info(cmd)
+ return Cmd.exec_cmd(cmd)
+
+ def init_ctrl(self):
+ """ Initialize controller and build controller attributes.
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ # initialize nqn and transport
+ cmd = "echo \"transport=" + self.transport + ",nqn=" + \
+ self.nqn + "\" > /dev/nvme-fabrics"
+ self.logger.info("Host Connect command : " + cmd)
+ if Cmd.exec_cmd(cmd) is False:
+ self.logger.info("ERROR : host connect command failed")
+ return False
+ self.ctrl_dev, self.ns_dev_list = self.build_ns_list()
+
+ if self.validate_fabric_ctrl() is False or self.id_ctrl() is False:
+ return False
+
+ return self.init_ns()
+
+ def id_ctrl(self):
+ """ Wrapper for executing id-ctrl command.
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ id_ctrl_cmd = "nvme id-ctrl " + self.ctrl_dev
+ proc = subprocess.Popen(id_ctrl_cmd,
+ shell=True,
+ stdout=subprocess.PIPE)
+ err = proc.wait()
+ if err != 0:
+ self.logger.error("nvme id-ctrl failed.")
+ return False
+
+ for line in proc.stdout:
+ if line.startswith('subnqn') or \
+ line.startswith('NVME Identify Controller'):
+ continue
+ if line.startswith('ps ') or line.strip().startswith('rwt'):
+ continue
+ key, val = line.split(':')
+ self.ctrl_dict[key.strip()] = val.strip()
+
+ self.logger.info("ID controller :- ")
+ self.logger.info(self.ctrl_dict)
+ return True
+
+ def id_ns(self):
+ """ Wrapper for executing id-ns command on all namespaces of this
+ controller.
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ for host_ns in iter(self):
+ if host_ns.id_ns() is False:
+ return False
+ return True
+
+ def ns_descs(self):
+ """ Wrapper for executing ns_descs command on all namespaces of this
+ controller.
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ for host_ns in iter(self):
+ if host_ns.ns_descs() is False:
+ return False
+ return True
+
+ def get_ns_id(self):
+ """ Wrapper for executing get-ns-id command on all namespaces of this
+ controller.
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ for host_ns in iter(self):
+ if host_ns.get_ns_id() is False:
+ return False
+ return True
+
+ def generate_next_ns_id(self):
+ """ Return next namespace id.
+ - Args :
+ - None.
+ - Returns :
+ - next namespace id.
+ """
+ return len(self.ns_list) + 1
+
+ def delete(self):
+ """ Delete subsystem and associated namespace(s).
+ - Args :
+ - None.
+ - Returns :
+ - True on success, False on failure.
+ """
+ self.logger.info("Deleting subsystem " + self.nqn + ".")
+ for host_ns in self.ns_list:
+ host_ns.delete()
+ cmd = "dirname $(grep -ls " + self.nqn + \
+ " /sys/class/nvme-fabrics/ctl/*/subsysnqn)"
+ try:
+ proc = subprocess.Popen(cmd,
+ shell=True,
+ stdout=subprocess.PIPE)
+ for line in proc.stdout:
+ line = line.strip('\n')
+ if not os.path.isdir(line):
+ self.logger.error("host ctrl dir " + self.nqn +
+ " not present.")
+ return False
+ cmd = "nvme disconnect -n " + self.nqn
+ self.logger.info("disconnecting : " + cmd)
+ ret = Cmd.exec_cmd(cmd)
+ if ret is False:
+ self.logger.error("failed to delete ctrl " +
+ self.nqn + ".")
+ return False
+ except Exception, err:
+ self.logger.error(str(err) + ".")
+ return False
+
+ return True
--
1.8.3.1
More information about the Linux-nvme
mailing list