def setUpClass(cls): udiskstestcase.UdisksTestCase.setUpClass() if not shutil.which("nvme"): udiskstestcase.UdisksTestCase.tearDownClass() raise unittest.SkipTest( "nvme executable (nvme-cli package) not found in $PATH, skipping." ) if not shutil.which("nvmetcli"): udiskstestcase.UdisksTestCase.tearDownClass() raise unittest.SkipTest( "nvmetcli executable not found in $PATH, skipping.") ret, out = udiskstestcase.run_command("modprobe nvme-fabrics") if ret != 0: raise unittest.SkipTest( "nvme-fabrics kernel module unavailable, skipping.") manager = cls.get_interface("/Manager", ".Manager") cls.loop_devs = [] for i in range(cls.NUM_NS): with tempfile.NamedTemporaryFile(prefix="udisks_test", delete=True, mode='w+b') as temp: temp.truncate(cls.NS_SIZE) loop_dev_obj_path = manager.LoopSetup(temp.fileno(), cls.no_options) path, loop_dev = loop_dev_obj_path.rsplit("/", 1) cls.loop_devs += ["/dev/%s" % loop_dev] cls.hostnqn = get_nvme_hostnqn() setup_nvme_target(cls.loop_devs, cls.SUBNQN)
def tearDownClass(cls): ret, out = udiskstestcase.run_command("nvmetcli clear") if ret != 0: raise RuntimeError("Error clearing the NVMe target: %s" % out) # detach loop devices for i in cls.loop_devs: cls.run_command("losetup --detach %s" % i) udiskstestcase.UdisksTestCase.tearDownClass()
def find_nvme_ns_devs_for_subnqn(subnqn): """ Find NVMe namespace block devices for the specified subsystem nqn :param str subnqn: subsystem nqn """ def _check_namespaces(node, ns_dev_paths): for ns in node['Namespaces']: path = os.path.join('/dev/', ns['NameSpace']) try: st = os.lstat(path) if stat.S_ISBLK(st.st_mode): ns_dev_paths += [path] except: pass def _check_subsys(subsys, ns_dev_paths): if subsys['SubsystemNQN'] == subnqn: if 'Namespaces' in subsys: _check_namespaces(subsys, ns_dev_paths) # kernel 4.18 if 'Controllers' in subsys: for ctrl in subsys['Controllers']: if 'Namespaces' in ctrl: _check_namespaces(ctrl, ns_dev_paths) ret, out = udiskstestcase.run_command( "nvme list --output-format=json --verbose") if ret != 0: raise RuntimeError("Error getting NVMe list: %s" % out) decoder = json.JSONDecoder() decoded = decoder.decode(out) if not decoded or 'Devices' not in decoded: return [] ns_dev_paths = [] for dev in decoded['Devices']: # nvme-cli 2.x if 'Subsystems' in dev: for subsys in dev['Subsystems']: _check_subsys(subsys, ns_dev_paths) # nvme-cli 1.x if 'SubsystemNQN' in dev: _check_subsys(dev, ns_dev_paths) return ns_dev_paths
def get_nvme_hostnqn(): """ Retrieves NVMe host NQN string from /etc/nvme/hostnqn or uses nvme-cli to generate new one (stable, typically generated from machine DMI data) when not available. """ hostnqn = None try: hostnqn = read_file('/etc/nvme/hostnqn') except: pass if hostnqn is None or len(hostnqn.strip()) < 1: ret, hostnqn = udiskstestcase.run_command('nvme gen-hostnqn') if ret != 0: raise RuntimeError("Cannot get host NQN: '%s %s'" % (hostnqn, err)) return hostnqn.strip()
def find_nvme_ctrl_devs_for_subnqn(subnqn): """ Find NVMe controller devices for the specified subsystem nqn :param str subnqn: subsystem nqn """ def _check_subsys(subsys, dev_paths): if subsys['SubsystemNQN'] == subnqn: for ctrl in subsys['Controllers']: path = os.path.join('/dev/', ctrl['Controller']) try: st = os.lstat(path) # nvme controller node is a character device if stat.S_ISCHR(st.st_mode): dev_paths += [path] except: pass ret, out = udiskstestcase.run_command( "nvme list --output-format=json --verbose") if ret != 0: raise RuntimeError("Error getting NVMe list: %s" % out) decoder = json.JSONDecoder() decoded = decoder.decode(out) if not decoded or 'Devices' not in decoded: return [] dev_paths = [] for dev in decoded['Devices']: # nvme-cli 2.x if 'Subsystems' in dev: for subsys in dev['Subsystems']: _check_subsys(subsys, dev_paths) # nvme-cli 1.x if 'SubsystemNQN' in dev: _check_subsys(dev, dev_paths) return dev_paths
def setup_nvme_target(dev_paths, subnqn): """ Sets up a new NVMe target loop device (using nvmetcli) on top of the :param:`dev_paths` backing block devices. :param set dev_paths: set of backing block device paths :param str subnqn: Subsystem NQN """ # modprobe required nvme target modules for module in ['nvmet', 'nvme-loop']: ret, out = udiskstestcase.run_command("modprobe %s" % module) if ret != 0: raise RuntimeError("Cannot load required NVMe target modules: %s" % out) # create a JSON file for nvmetcli with tempfile.NamedTemporaryFile(mode='wt', delete=False) as tmp: tcli_json_file = tmp.name namespaces = ",".join([ """ {{ "device": {{ "nguid": "{nguid}", "path": "{path}" }}, "enable": 1, "nsid": {nsid} }} """.format(nguid=uuid.uuid4(), path=dev_path, nsid=i) for i, dev_path in enumerate(dev_paths, start=1) ]) json = """ { "ports": [ { "addr": { "adrfam": "", "traddr": "", "treq": "not specified", "trsvcid": "", "trtype": "loop" }, "portid": 1, "referrals": [], "subsystems": [ "%s" ] } ], "subsystems": [ { "attr": { "allow_any_host": "1" }, "namespaces": [ %s ], "nqn": "%s" } ] } """ tmp.write(json % (subnqn, namespaces, subnqn)) ret, out = udiskstestcase.run_command("nvmetcli restore %s" % tcli_json_file) os.unlink(tcli_json_file) if ret != 0: raise RuntimeError("Error setting up the NVMe target: %s" % out)