#!/usr/bin/env python3 import os import os.path import re import sys import time import json import random from subprocess import check_call, call, check_output, Popen, PIPE, CalledProcessError if (len(sys.argv) == 8): target_ip = sys.argv[2] initiator_ip = sys.argv[3] port = sys.argv[4] netmask = sys.argv[5] namespace = sys.argv[6] test_type = sys.argv[7] ns_cmd = 'ip netns exec ' + namespace other_ip = '127.0.0.6' initiator_name = 'ANY' portal_tag = '1' initiator_tag = '1' rpc_param = { 'target_ip': target_ip, 'initiator_ip': initiator_ip, 'port': port, 'initiator_name': initiator_name, 'netmask': netmask, 'lun_total': 3, 'malloc_bdev_size': 64, 'malloc_block_size': 512, 'queue_depth': 64, 'target_name': 'Target3', 'alias_name': 'Target3_alias', 'disable_chap': True, 'mutual_chap': False, 'require_chap': False, 'chap_group': 0, 'header_digest': False, 'data_digest': False, 'log_flag': 'rpc', 'cpumask': 0x1 } class RpcException(Exception): def __init__(self, retval, msg): super(RpcException, self).__init__(msg) self.retval = retval self.message = msg class spdk_rpc(object): def __init__(self, rpc_py): self.rpc_py = rpc_py def __getattr__(self, name): def call(*args): cmd = "{} {}".format(self.rpc_py, name) for arg in args: cmd += " {}".format(arg) return check_output(cmd, shell=True).decode("utf-8") return call def verify(expr, retcode, msg): if not expr: raise RpcException(retcode, msg) def verify_log_flag_rpc_methods(rpc_py, rpc_param): rpc = spdk_rpc(rpc_py) output = rpc.log_get_flags() jsonvalue = json.loads(output) verify(not jsonvalue[rpc_param['log_flag']], 1, "log_get_flags returned {}, expected false".format(jsonvalue)) rpc.log_set_flag(rpc_param['log_flag']) output = rpc.log_get_flags() jsonvalue = json.loads(output) verify(jsonvalue[rpc_param['log_flag']], 1, "log_get_flags returned {}, expected true".format(jsonvalue)) rpc.log_clear_flag(rpc_param['log_flag']) output = rpc.log_get_flags() jsonvalue = json.loads(output) verify(not jsonvalue[rpc_param['log_flag']], 1, "log_get_flags returned {}, expected false".format(jsonvalue)) print("verify_log_flag_rpc_methods passed") def verify_iscsi_connection_rpc_methods(rpc_py): rpc = spdk_rpc(rpc_py) output = rpc.iscsi_get_connections() jsonvalue = json.loads(output) verify(not jsonvalue, 1, "iscsi_get_connections returned {}, expected empty".format(jsonvalue)) rpc.bdev_malloc_create(rpc_param['malloc_bdev_size'], rpc_param['malloc_block_size']) rpc.iscsi_create_portal_group(portal_tag, "{}:{}".format(rpc_param['target_ip'], str(rpc_param['port']))) rpc.iscsi_create_initiator_group(initiator_tag, rpc_param['initiator_name'], rpc_param['netmask']) lun_mapping = "Malloc" + str(rpc_param['lun_total']) + ":0" net_mapping = portal_tag + ":" + initiator_tag rpc.iscsi_create_target_node(rpc_param['target_name'], rpc_param['alias_name'], lun_mapping, net_mapping, rpc_param['queue_depth'], '-d') check_output('iscsiadm -m discovery -t st -p {}'.format(rpc_param['target_ip']), shell=True) check_output('iscsiadm -m node --login', shell=True) name = json.loads(rpc.iscsi_get_target_nodes())[0]['name'] output = rpc.iscsi_get_connections() jsonvalues = json.loads(output) verify(jsonvalues[0]['target_node_name'] == rpc_param['target_name'], 1, "target node name vaule is {}, expected {}".format(jsonvalues[0]['target_node_name'], rpc_param['target_name'])) verify(jsonvalues[0]['initiator_addr'] == rpc_param['initiator_ip'], 1, "initiator address values is {}, expected {}".format(jsonvalues[0]['initiator_addr'], rpc_param['initiator_ip'])) verify(jsonvalues[0]['target_addr'] == rpc_param['target_ip'], 1, "target address values is {}, expected {}".format(jsonvalues[0]['target_addr'], rpc_param['target_ip'])) check_output('iscsiadm -m node --logout', shell=True) check_output('iscsiadm -m node -o delete', shell=True) rpc.iscsi_delete_initiator_group(initiator_tag) rpc.iscsi_delete_portal_group(portal_tag) rpc.iscsi_delete_target_node(name) output = rpc.iscsi_get_connections() jsonvalues = json.loads(output) verify(not jsonvalues, 1, "iscsi_get_connections returned {}, expected empty".format(jsonvalues)) print("verify_iscsi_connection_rpc_methods passed") def verify_scsi_devices_rpc_methods(rpc_py): rpc = spdk_rpc(rpc_py) output = rpc.scsi_get_devices() jsonvalue = json.loads(output) verify(not jsonvalue, 1, "scsi_get_devices returned {}, expected empty".format(jsonvalue)) rpc.bdev_malloc_create(rpc_param['malloc_bdev_size'], rpc_param['malloc_block_size']) rpc.iscsi_create_portal_group(portal_tag, "{}:{}".format(rpc_param['target_ip'], str(rpc_param['port']))) rpc.iscsi_create_initiator_group(initiator_tag, rpc_param['initiator_name'], rpc_param['netmask']) lun_mapping = "Malloc" + str(rpc_param['lun_total']) + ":0" net_mapping = portal_tag + ":" + initiator_tag rpc.iscsi_create_target_node(rpc_param['target_name'], rpc_param['alias_name'], lun_mapping, net_mapping, rpc_param['queue_depth'], '-d') check_output('iscsiadm -m discovery -t st -p {}'.format(rpc_param['target_ip']), shell=True) check_output('iscsiadm -m node --login', shell=True) name = json.loads(rpc.iscsi_get_target_nodes())[0]['name'] output = rpc.iscsi_get_options() jsonvalues = json.loads(output) nodebase = jsonvalues['node_base'] output = rpc.scsi_get_devices() jsonvalues = json.loads(output) verify(jsonvalues[0]['device_name'] == nodebase + ":" + rpc_param['target_name'], 1, "device name vaule is {}, expected {}".format(jsonvalues[0]['device_name'], rpc_param['target_name'])) verify(jsonvalues[0]['id'] == 0, 1, "device id value is {}, expected 0".format(jsonvalues[0]['id'])) check_output('iscsiadm -m node --logout', shell=True) check_output('iscsiadm -m node -o delete', shell=True) rpc.iscsi_delete_initiator_group(initiator_tag) rpc.iscsi_delete_portal_group(portal_tag) rpc.iscsi_delete_target_node(name) output = rpc.scsi_get_devices() jsonvalues = json.loads(output) verify(not jsonvalues, 1, "scsi_get_devices returned {}, expected empty".format(jsonvalues)) print("verify_scsi_devices_rpc_methods passed") def create_malloc_bdevs_rpc_methods(rpc_py, rpc_param): rpc = spdk_rpc(rpc_py) for i in range(1, rpc_param['lun_total'] + 1): rpc.bdev_malloc_create(rpc_param['malloc_bdev_size'], rpc_param['malloc_block_size']) print("create_malloc_bdevs_rpc_methods passed") def verify_portal_groups_rpc_methods(rpc_py, rpc_param): rpc = spdk_rpc(rpc_py) output = rpc.iscsi_get_portal_groups() jsonvalues = json.loads(output) verify(not jsonvalues, 1, "iscsi_get_portal_groups returned {} groups, expected empty".format(jsonvalues)) lo_ip = (target_ip, other_ip) nics = json.loads(rpc.net_get_interfaces()) for x in nics: if x["ifc_index"] == 'lo': rpc.net_interface_add_ip_address(x["ifc_index"], lo_ip[1]) for idx, value in enumerate(lo_ip): # The portal group tag must start at 1 tag = idx + 1 rpc.iscsi_create_portal_group(tag, "{}:{}".format(value, rpc_param['port'])) output = rpc.iscsi_get_portal_groups() jsonvalues = json.loads(output) verify(len(jsonvalues) == tag, 1, "iscsi_get_portal_groups returned {} groups, expected {}".format(len(jsonvalues), tag)) tag_list = [] for idx, value in enumerate(jsonvalues): verify(value['portals'][0]['host'] == lo_ip[idx], 1, "host value is {}, expected {}".format(value['portals'][0]['host'], rpc_param['target_ip'])) verify(value['portals'][0]['port'] == str(rpc_param['port']), 1, "port value is {}, expected {}".format(value['portals'][0]['port'], str(rpc_param['port']))) tag_list.append(value['tag']) verify(value['tag'] == idx + 1, 1, "tag value is {}, expected {}".format(value['tag'], idx + 1)) for idx, value in enumerate(tag_list): rpc.iscsi_delete_portal_group(value) output = rpc.iscsi_get_portal_groups() jsonvalues = json.loads(output) verify(len(jsonvalues) == (len(tag_list) - (idx + 1)), 1, "get_portal_group returned {} groups, expected {}".format(len(jsonvalues), (len(tag_list) - (idx + 1)))) if not jsonvalues: break for jidx, jvalue in enumerate(jsonvalues): verify(jvalue['portals'][0]['host'] == lo_ip[idx + jidx + 1], 1, "host value is {}, expected {}".format(jvalue['portals'][0]['host'], lo_ip[idx + jidx + 1])) verify(jvalue['portals'][0]['port'] == str(rpc_param['port']), 1, "port value is {}, expected {}".format(jvalue['portals'][0]['port'], str(rpc_param['port']))) verify(jvalue['tag'] != value or jvalue['tag'] == tag_list[idx + jidx + 1], 1, "tag value is {}, expected {} and not {}".format(jvalue['tag'], tag_list[idx + jidx + 1], value)) for x in nics: if x["ifc_index"] == 'lo': rpc.net_interface_delete_ip_address(x["ifc_index"], lo_ip[1]) print("verify_portal_groups_rpc_methods passed") def verify_initiator_groups_rpc_methods(rpc_py, rpc_param): rpc = spdk_rpc(rpc_py) output = rpc.iscsi_get_initiator_groups() jsonvalues = json.loads(output) verify(not jsonvalues, 1, "iscsi_get_initiator_groups returned {}, expected empty".format(jsonvalues)) for idx, value in enumerate(rpc_param['netmask']): # The initiator group tag must start at 1 tag = idx + 1 rpc.iscsi_create_initiator_group(tag, rpc_param['initiator_name'], value) output = rpc.iscsi_get_initiator_groups() jsonvalues = json.loads(output) verify(len(jsonvalues) == tag, 1, "iscsi_get_initiator_groups returned {} groups, expected {}".format(len(jsonvalues), tag)) tag_list = [] for idx, value in enumerate(jsonvalues): verify(value['initiators'][0] == rpc_param['initiator_name'], 1, "initiator value is {}, expected {}".format(value['initiators'][0], rpc_param['initiator_name'])) tag_list.append(value['tag']) verify(value['tag'] == idx + 1, 1, "tag value is {}, expected {}".format(value['tag'], idx + 1)) verify(value['netmasks'][0] == rpc_param['netmask'][idx], 1, "netmasks value is {}, expected {}".format(value['netmasks'][0], rpc_param['netmask'][idx])) for idx, value in enumerate(rpc_param['netmask']): tag = idx + 1 rpc.iscsi_initiator_group_remove_initiators(tag, '-n', rpc_param['initiator_name'], '-m', value) output = rpc.iscsi_get_initiator_groups() jsonvalues = json.loads(output) verify(len(jsonvalues) == tag, 1, "iscsi_get_initiator_groups returned {} groups, expected {}".format(len(jsonvalues), tag)) for idx, value in enumerate(jsonvalues): verify(value['tag'] == idx + 1, 1, "tag value is {}, expected {}".format(value['tag'], idx + 1)) initiators = value.get('initiators') verify(len(initiators) == 0, 1, "length of initiator list is {}, expected 0".format(len(initiators))) netmasks = value.get('netmasks') verify(len(netmasks) == 0, 1, "length of netmask list is {}, expected 0".format(len(netmasks))) for idx, value in enumerate(rpc_param['netmask']): tag = idx + 1 rpc.iscsi_initiator_group_add_initiators(tag, '-n', rpc_param['initiator_name'], '-m', value) output = rpc.iscsi_get_initiator_groups() jsonvalues = json.loads(output) verify(len(jsonvalues) == tag, 1, "iscsi_get_initiator_groups returned {} groups, expected {}".format(len(jsonvalues), tag)) tag_list = [] for idx, value in enumerate(jsonvalues): verify(value['initiators'][0] == rpc_param['initiator_name'], 1, "initiator value is {}, expected {}".format(value['initiators'][0], rpc_param['initiator_name'])) tag_list.append(value['tag']) verify(value['tag'] == idx + 1, 1, "tag value is {}, expected {}".format(value['tag'], idx + 1)) verify(value['netmasks'][0] == rpc_param['netmask'][idx], 1, "netmasks value is {}, expected {}".format(value['netmasks'][0], rpc_param['netmask'][idx])) for idx, value in enumerate(tag_list): rpc.iscsi_delete_initiator_group(value) output = rpc.iscsi_get_initiator_groups() jsonvalues = json.loads(output) verify(len(jsonvalues) == (len(tag_list) - (idx + 1)), 1, "iscsi_get_initiator_groups returned {} groups, expected {}".format(len(jsonvalues), (len(tag_list) - (idx + 1)))) if not jsonvalues: break for jidx, jvalue in enumerate(jsonvalues): verify(jvalue['initiators'][0] == rpc_param['initiator_name'], 1, "initiator value is {}, expected {}".format(jvalue['initiators'][0], rpc_param['initiator_name'])) verify(jvalue['tag'] != value or jvalue['tag'] == tag_list[idx + jidx + 1], 1, "tag value is {}, expected {} and not {}".format(jvalue['tag'], tag_list[idx + jidx + 1], value)) verify(jvalue['netmasks'][0] == rpc_param['netmask'][idx + jidx + 1], 1, "netmasks value is {}, expected {}".format(jvalue['netmasks'][0], rpc_param['netmask'][idx + jidx + 1])) print("verify_initiator_groups_rpc_method passed.") def verify_target_nodes_rpc_methods(rpc_py, rpc_param): rpc = spdk_rpc(rpc_py) output = rpc.iscsi_get_options() jsonvalues = json.loads(output) nodebase = jsonvalues['node_base'] output = rpc.iscsi_get_target_nodes() jsonvalues = json.loads(output) verify(not jsonvalues, 1, "iscsi_get_target_nodes returned {}, expected empty".format(jsonvalues)) rpc.bdev_malloc_create(rpc_param['malloc_bdev_size'], rpc_param['malloc_block_size']) rpc.iscsi_create_portal_group(portal_tag, "{}:{}".format(rpc_param['target_ip'], str(rpc_param['port']))) rpc.iscsi_create_initiator_group(initiator_tag, rpc_param['initiator_name'], rpc_param['netmask']) lun_mapping = "Malloc" + str(rpc_param['lun_total']) + ":0" net_mapping = portal_tag + ":" + initiator_tag rpc.iscsi_create_target_node(rpc_param['target_name'], rpc_param['alias_name'], lun_mapping, net_mapping, rpc_param['queue_depth'], '-d') output = rpc.iscsi_get_target_nodes() jsonvalues = json.loads(output) verify(len(jsonvalues) == 1, 1, "iscsi_get_target_nodes returned {} nodes, expected 1".format(len(jsonvalues))) bdev_name = jsonvalues[0]['luns'][0]['bdev_name'] verify(bdev_name == "Malloc" + str(rpc_param['lun_total']), 1, "bdev_name value is {}, expected Malloc{}".format(jsonvalues[0]['luns'][0]['bdev_name'], str(rpc_param['lun_total']))) name = jsonvalues[0]['name'] verify(name == nodebase + ":" + rpc_param['target_name'], 1, "target name value is {}, expected {}".format(name, nodebase + ":" + rpc_param['target_name'])) verify(jsonvalues[0]['alias_name'] == rpc_param['alias_name'], 1, "target alias_name value is {}, expected {}".format(jsonvalues[0]['alias_name'], rpc_param['alias_name'])) verify(jsonvalues[0]['luns'][0]['lun_id'] == 0, 1, "lun id value is {}, expected 0".format(jsonvalues[0]['luns'][0]['lun_id'])) verify(jsonvalues[0]['pg_ig_maps'][0]['ig_tag'] == int(initiator_tag), 1, "initiator group tag value is {}, expected {}".format(jsonvalues[0]['pg_ig_maps'][0]['ig_tag'], initiator_tag)) verify(jsonvalues[0]['queue_depth'] == rpc_param['queue_depth'], 1, "queue depth value is {}, expected {}".format(jsonvalues[0]['queue_depth'], rpc_param['queue_depth'])) verify(jsonvalues[0]['pg_ig_maps'][0]['pg_tag'] == int(portal_tag), 1, "portal group tag value is {}, expected {}".format(jsonvalues[0]['pg_ig_maps'][0]['pg_tag'], portal_tag)) verify(jsonvalues[0]['disable_chap'] == rpc_param['disable_chap'], 1, "disable chap value is {}, expected {}".format(jsonvalues[0]['disable_chap'], rpc_param['disable_chap'])) verify(jsonvalues[0]['mutual_chap'] == rpc_param['mutual_chap'], 1, "chap mutual value is {}, expected {}".format(jsonvalues[0]['mutual_chap'], rpc_param['mutual_chap'])) verify(jsonvalues[0]['require_chap'] == rpc_param['require_chap'], 1, "chap required value is {}, expected {}".format(jsonvalues[0]['require_chap'], rpc_param['require_chap'])) verify(jsonvalues[0]['chap_group'] == rpc_param['chap_group'], 1, "chap auth group value is {}, expected {}".format(jsonvalues[0]['chap_group'], rpc_param['chap_group'])) verify(jsonvalues[0]['header_digest'] == rpc_param['header_digest'], 1, "header digest value is {}, expected {}".format(jsonvalues[0]['header_digest'], rpc_param['header_digest'])) verify(jsonvalues[0]['data_digest'] == rpc_param['data_digest'], 1, "data digest value is {}, expected {}".format(jsonvalues[0]['data_digest'], rpc_param['data_digest'])) lun_id = '1' rpc.iscsi_target_node_add_lun(name, bdev_name, "-i", lun_id) output = rpc.iscsi_get_target_nodes() jsonvalues = json.loads(output) verify(jsonvalues[0]['luns'][1]['bdev_name'] == "Malloc" + str(rpc_param['lun_total']), 1, "bdev_name value is {}, expected Malloc{}".format(jsonvalues[0]['luns'][0]['bdev_name'], str(rpc_param['lun_total']))) verify(jsonvalues[0]['luns'][1]['lun_id'] == 1, 1, "lun id value is {}, expected 1".format(jsonvalues[0]['luns'][1]['lun_id'])) rpc.iscsi_delete_target_node(name) output = rpc.iscsi_get_target_nodes() jsonvalues = json.loads(output) verify(not jsonvalues, 1, "iscsi_get_target_nodes returned {}, expected empty".format(jsonvalues)) rpc.iscsi_create_target_node(rpc_param['target_name'], rpc_param['alias_name'], lun_mapping, net_mapping, rpc_param['queue_depth'], '-d') rpc.iscsi_delete_portal_group(portal_tag) rpc.iscsi_delete_initiator_group(initiator_tag) rpc.iscsi_delete_target_node(name) output = rpc.iscsi_get_target_nodes() jsonvalues = json.loads(output) if not jsonvalues: print("This issue will be fixed later.") print("verify_target_nodes_rpc_methods passed.") def verify_net_get_interfaces(rpc_py): rpc = spdk_rpc(rpc_py) nics = json.loads(rpc.net_get_interfaces()) nics_names = set(x["name"] for x in nics) # parse ip link show to verify the net_get_interfaces result ip_show = ns_cmd + " ip link show" ifcfg_nics = set(re.findall(r'\S+:\s(\S+?)(?:@\S+){0,1}:\s<.*', check_output(ip_show.split()).decode())) verify(nics_names == ifcfg_nics, 1, "net_get_interfaces returned {}".format(nics)) print("verify_net_get_interfaces passed.") def help_get_interface_ip_list(rpc_py, nic_name): rpc = spdk_rpc(rpc_py) nics = json.loads(rpc.net_get_interfaces()) nic = list([x for x in nics if x["name"] == nic_name]) verify(len(nic) != 0, 1, "Nic name: {} is not found in {}".format(nic_name, [x["name"] for x in nics])) return nic[0]["ip_addr"] def verify_net_interface_add_delete_ip_address(rpc_py): rpc = spdk_rpc(rpc_py) nics = json.loads(rpc.net_get_interfaces()) # add ip on up to first 2 nics for x in nics[:2]: faked_ip = "123.123.{}.{}".format(random.randint(1, 254), random.randint(1, 254)) ping_cmd = ns_cmd + " ping -c 1 -W 1 " + faked_ip rpc.net_interface_add_ip_address(x["ifc_index"], faked_ip) verify(faked_ip in help_get_interface_ip_list(rpc_py, x["name"]), 1, "add ip {} to nic {} failed.".format(faked_ip, x["name"])) try: check_call(ping_cmd.split()) except BaseException: verify(False, 1, "ping ip {} for {} was failed(adding was successful)".format (faked_ip, x["name"])) rpc.net_interface_delete_ip_address(x["ifc_index"], faked_ip) verify(faked_ip not in help_get_interface_ip_list(rpc_py, x["name"]), 1, "delete ip {} from nic {} failed.(adding and ping were successful)".format (faked_ip, x["name"])) # ping should be failed and throw an CalledProcessError exception try: check_call(ping_cmd.split()) except CalledProcessError as _: pass except Exception as e: verify(False, 1, "Unexpected exception was caught {}(adding/ping/delete were successful)".format (str(e))) else: verify(False, 1, "ip {} for {} could be pinged after delete ip(adding/ping/delete were successful)".format (faked_ip, x["name"])) print("verify_net_interface_add_delete_ip_address passed.") if __name__ == "__main__": rpc_py = sys.argv[1] try: verify_log_flag_rpc_methods(rpc_py, rpc_param) verify_net_get_interfaces(rpc_py) # Add/delete IP will not be supported in VPP. # It has separate vppctl utility for that. if test_type == 'posix': verify_net_interface_add_delete_ip_address(rpc_py) create_malloc_bdevs_rpc_methods(rpc_py, rpc_param) verify_portal_groups_rpc_methods(rpc_py, rpc_param) verify_initiator_groups_rpc_methods(rpc_py, rpc_param) verify_target_nodes_rpc_methods(rpc_py, rpc_param) verify_scsi_devices_rpc_methods(rpc_py) verify_iscsi_connection_rpc_methods(rpc_py) except RpcException as e: print("{}. Exiting with status {}".format(e.message, e.retval)) raise e except Exception as e: raise e sys.exit(0)