diff --git a/control/cli.py b/control/cli.py index 7dffeab2f..c022ef530 100644 --- a/control/cli.py +++ b/control/cli.py @@ -296,53 +296,84 @@ def remove_host(self, args): argument("-n", "--subnqn", help="Subsystem NQN", required=True), argument("-g", "--gateway-name", help="Gateway name", required=True), argument("-t", "--trtype", help="Transport type", default="TCP"), - argument("-f", "--adrfam", help="Address family", default="ipv4"), + argument("-f", "--adrfam", help="Address family", default="IPV4"), argument("-a", "--traddr", help="NVMe host IP", required=True), argument("-s", "--trsvcid", help="Port number", default="4420", required=False), ]) def create_listener(self, args): """Creates a listener for a subsystem at a given IP/Port.""" traddr = GatewayConfig.escape_address_if_ipv6(args.traddr) - req = pb2.create_listener_req( - nqn=args.subnqn, - gateway_name=args.gateway_name, - trtype=args.trtype, - adrfam=args.adrfam, - traddr=traddr, - trsvcid=args.trsvcid, - ) - ret = self.stub.create_listener(req) - self.logger.info(f"Created {args.subnqn} listener at {traddr}:{args.trsvcid}: {ret.status}") + trtype = None + adrfam = None + if args.trtype: + trtype = args.trtype.upper() + if args.adrfam: + adrfam = args.adrfam.lower() + try: + req = pb2.create_listener_req( + nqn=args.subnqn, + gateway_name=args.gateway_name, + trtype=trtype, + adrfam=adrfam, + traddr=traddr, + trsvcid=args.trsvcid, + ) + ret = self.stub.create_listener(req) + self.logger.info(f"Created {args.subnqn} listener at {traddr}:{args.trsvcid}: {ret.status}") + except ValueError as err: + self.logger.error(f"{err}") + self.logger.info(f"Created {args.subnqn} listener at {traddr}:{args.trsvcid}: {False}") + raise + except Exception as ex: + self.logger.info(f"Created {args.subnqn} listener at {traddr}:{args.trsvcid}: {False}") + raise @cli.cmd([ argument("-n", "--subnqn", help="Subsystem NQN", required=True), argument("-g", "--gateway-name", help="Gateway name", required=True), argument("-t", "--trtype", help="Transport type", default="TCP"), - argument("-f", "--adrfam", help="Address family", default="ipv4"), + argument("-f", "--adrfam", help="Address family", default="IPV4"), argument("-a", "--traddr", help="NVMe host IP", required=True), argument("-s", "--trsvcid", help="Port number", default="4420", required=False), ]) def delete_listener(self, args): """Deletes a listener from a subsystem at a given IP/Port.""" traddr = GatewayConfig.escape_address_if_ipv6(args.traddr) - req = pb2.delete_listener_req( - nqn=args.subnqn, - gateway_name=args.gateway_name, - trtype=args.trtype, - adrfam=args.adrfam, - traddr=traddr, - trsvcid=args.trsvcid, - ) - ret = self.stub.delete_listener(req) - self.logger.info(f"Deleted {traddr}:{args.trsvcid} from {args.subnqn}: {ret.status}") + trtype = None + adrfam = None + if args.trtype: + trtype = args.trtype.upper() + if args.adrfam: + adrfam = args.adrfam.lower() + try: + req = pb2.delete_listener_req( + nqn=args.subnqn, + gateway_name=args.gateway_name, + trtype=trtype, + adrfam=adrfam, + traddr=traddr, + trsvcid=args.trsvcid, + ) + ret = self.stub.delete_listener(req) + self.logger.info(f"Deleted {traddr}:{args.trsvcid} from {args.subnqn}: {ret.status}") + except ValueError as err: + self.logger.error(f"{err}") + self.logger.info(f"Deleted {traddr}:{args.trsvcid} from {args.subnqn}: {False}") + raise + except Exception as ex: + self.logger.info(f"Deleted {traddr}:{args.trsvcid} from {args.subnqn}: {False}") + raise @cli.cmd() def get_subsystems(self, args): """Gets subsystems.""" subsystems = json_format.MessageToJson( self.stub.get_subsystems(pb2.get_subsystems_req()), - indent=4, + indent=4, including_default_value_fields=True, preserving_proto_field_name=True) + # The address family enum values are lower case, convert them for display + subsystems = subsystems.replace('"adrfam": "ipv4"', '"adrfam": "IPv4"') + subsystems = subsystems.replace('"adrfam": "ipv6"', '"adrfam": "IPv6"') self.logger.info(f"Get subsystems:\n{subsystems}") @cli.cmd() @@ -363,8 +394,6 @@ def disable_spdk_nvmf_logs(self, args): f"Disable SPDK nvmf logs: {ret.status}") @cli.cmd([ - argument("-f", "--flags", help="SPDK nvmf enable flags", \ - action='store_true', required=True), argument("-l", "--log_level", \ help="SPDK nvmf log level (ERROR, WARNING, NOTICE, INFO, DEBUG)", required=False), argument("-p", "--log_print_level", \ @@ -372,12 +401,24 @@ def disable_spdk_nvmf_logs(self, args): required=False), ]) def set_spdk_nvmf_logs(self, args): - """Set spdk nvmf log and flags""" - req = pb2.set_spdk_nvmf_logs_req(flags=args.flags, log_level=args.log_level, \ - print_level=args.log_print_level) - ret = self.stub.set_spdk_nvmf_logs(req) - self.logger.info( - f"Set SPDK nvmf logs : {ret.status}") + """Set spdk nvmf log and print levels""" + log_level = None + print_level = None + if args.log_level: + log_level = args.log_level.upper() + if args.log_print_level: + print_level = args.log_print_level.upper() + try: + req = pb2.set_spdk_nvmf_logs_req(log_level=log_level, print_level=print_level) + ret = self.stub.set_spdk_nvmf_logs(req) + self.logger.info(f"Set SPDK nvmf logs: {ret.status}") + except ValueError as err: + self.logger.error(f"{err}") + self.logger.info(f"Set SPDK nvmf logs: {False}") + raise + except Exception as ex: + self.logger.info(f"Set SPDK nvmf logs: {False}") + raise @cli.cmd() def get_gateway_info(self, args): diff --git a/control/config.py b/control/config.py index ccec4fc62..3ef2ab444 100644 --- a/control/config.py +++ b/control/config.py @@ -16,6 +16,9 @@ class GatewayConfig: Instance attributes: config: Config parser object """ + + DISCOVERY_NQN = "nqn.2014-08.org.nvmexpress.discovery" + def __init__(self, conffile): self.filepath = conffile self.conffile_logged = False diff --git a/control/discovery.py b/control/discovery.py index 81d85f6b6..c1ad81b1b 100644 --- a/control/discovery.py +++ b/control/discovery.py @@ -13,6 +13,8 @@ import logging from .config import GatewayConfig from .state import GatewayState, LocalGatewayState, OmapGatewayState, GatewayStateHandler +from .grpc import GatewayEnumUtils +from .proto import gateway_pb2 as pb2 import rados from typing import Dict, Optional @@ -82,21 +84,6 @@ class NVMF_SUBTYPE(enum.IntFlag): # NVMe type for NVM subsystem NVME = 0x2 -# NVMe over Fabrics transport types -class TRANSPORT_TYPES(enum.IntFlag): - RDMA = 0x1 - FC = 0x2 - TCP = 0x3 - INTRA_HOST = 0xfe - -# Address family types -class ADRFAM_TYPES(enum.IntFlag): - ipv4 = 0x1 - ipv6 = 0x2 - ib = 0x3 - fc = 0x4 - intra_host = 0xfe - # Transport requirement, secure channel requirements # Connections shall be made over a fabric secure channel class NVMF_TREQ_SECURE_CHANNEL(enum.IntFlag): @@ -304,8 +291,6 @@ class DiscoveryService: discovery_port: Discovery controller's listening port """ - DISCOVERY_NQN = "nqn.2014-08.org.nvmexpress.discovery" - def __init__(self, config): self.version = 1 self.config = config @@ -733,14 +718,18 @@ def reply_get_log_page(self, conn, data, cmd_id): log_entry_counter = 0 while log_entry_counter < len(allow_listeners): log_entry = DiscoveryLogEntry() - trtype = TRANSPORT_TYPES[allow_listeners[log_entry_counter]["trtype"].upper()] + log_trtype = allow_listeners[log_entry_counter]["trtype"] + log_adrfam = allow_listeners[log_entry_counter]["adrfam"] + trtype = GatewayEnumUtils.get_value_from_key(pb2.TransportType, log_trtype, True) + adrfam = GatewayEnumUtils.get_value_from_key(pb2.AddressFamily, log_adrfam, True) + if trtype is None: - self.logger.error("unsupported transport type") + self.logger.error(f"unsupported transport type {log_trtype}") else: log_entry.trtype = trtype - adrfam = ADRFAM_TYPES[allow_listeners[log_entry_counter]["adrfam"].lower()] + if adrfam is None: - self.logger.error("unsupported adress family") + self.logger.error(f"unsupported address family {log_adrfam}") else: log_entry.adrfam = adrfam log_entry.subtype = NVMF_SUBTYPE.NVME diff --git a/control/grpc.py b/control/grpc.py index a6b994b08..457059abc 100644 --- a/control/grpc.py +++ b/control/grpc.py @@ -25,11 +25,39 @@ from .proto import gateway_pb2 as pb2 from .proto import gateway_pb2_grpc as pb2_grpc from .config import GatewayConfig -from .discovery import DiscoveryService from .state import GatewayState MAX_ANA_GROUPS = 4 +class GatewayEnumUtils: + def get_value_from_key(e_type, keyval, ignore_case = False): + val = None + try: + key_index = e_type.keys().index(keyval) + val = e_type.values()[key_index] + except ValueError: + pass + except IndexError: + pass + + if ignore_case and val == None and type(keyval) == str: + val = get_value_from_key(e_type, keyval.lower(), False) + if ignore_case and val == None and type(keyval) == str: + val = get_value_from_key(e_type, keyval.upper(), False) + + return val + + def get_key_from_value(e_type, val): + keyval = None + try: + val_index = e_type.values().index(val) + keyval = e_type.keys()[val_index] + except ValueError: + pass + except IndexError: + pass + return keyval + class GatewayService(pb2_grpc.GatewayServicer): """Implements gateway service interface. @@ -166,7 +194,7 @@ def create_bdev_safe(self, request, context=None): # Update gateway state try: json_req = json_format.MessageToJson( - request, preserving_proto_field_name=True) + request, preserving_proto_field_name=True, including_default_value_fields=True) self.gateway_state.add_bdev(bdev_name, json_req) except Exception as ex: self.logger.error( @@ -282,7 +310,7 @@ def delete_bdev(self, request, context=None): return self.execute_grpc_function(self.delete_bdev_safe, request, context) def is_discovery_nqn(self, nqn) -> bool: - return nqn == DiscoveryService.DISCOVERY_NQN + return nqn == GatewayConfig.DISCOVERY_NQN def serial_number_already_used(self, context, serial) -> str: if not context: @@ -358,7 +386,7 @@ def create_subsystem_safe(self, request, context=None): # Update gateway state try: json_req = json_format.MessageToJson( - request, preserving_proto_field_name=True) + request, preserving_proto_field_name=True, including_default_value_fields=True) self.gateway_state.add_subsystem(request.subsystem_nqn, json_req) except Exception as ex: @@ -443,7 +471,7 @@ def add_namespace_safe(self, request, context=None): if not request.nsid: request.nsid = nsid json_req = json_format.MessageToJson( - request, preserving_proto_field_name=True) + request, preserving_proto_field_name=True, including_default_value_fields=True) self.gateway_state.add_namespace(request.subsystem_nqn, str(nsid), json_req) except Exception as ex: @@ -563,7 +591,7 @@ def add_host_safe(self, request, context=None): # Update gateway state try: json_req = json_format.MessageToJson( - request, preserving_proto_field_name=True) + request, preserving_proto_field_name=True, including_default_value_fields=True) self.gateway_state.add_host(request.subsystem_nqn, request.host_nqn, json_req) except Exception as ex: @@ -642,9 +670,18 @@ def create_listener_safe(self, request, context=None): """Creates a listener for a subsystem at a given IP/Port.""" ret = True traddr = GatewayConfig.escape_address_if_ipv6(request.traddr) + + trtype = GatewayEnumUtils.get_key_from_value(pb2.TransportType, request.trtype) + if trtype == None: + raise Exception(f"Unknown transport type {request.trtype}") + + adrfam = GatewayEnumUtils.get_key_from_value(pb2.AddressFamily, request.adrfam) + if adrfam == None: + raise Exception(f"Unknown address family {request.adrfam}") + self.logger.info(f"Received request to create {request.gateway_name}" - f" {request.trtype} {request.adrfam} listener for {request.nqn} at" - f" {traddr}:{request.trsvcid}., context: {context}") + f" {trtype} {adrfam} listener for {request.nqn} at" + f" {traddr}:{request.trsvcid}, context: {context}") if self.is_discovery_nqn(request.nqn): raise Exception(f"Can't create a listener for a discovery subsystem") @@ -653,12 +690,12 @@ def create_listener_safe(self, request, context=None): try: if request.gateway_name == self.gateway_name: listener_already_exist = self.matching_listener_exists( - context, request.nqn, request.gateway_name, request.trtype, request.traddr, request.trsvcid) + context, request.nqn, request.gateway_name, trtype, request.traddr, request.trsvcid) if listener_already_exist: self.logger.error(f"{request.nqn} already listens on address {request.traddr} port {request.trsvcid}") - req = {"nqn": request.nqn, "trtype": request.trtype, "traddr": request.traddr, + req = {"nqn": request.nqn, "trtype": trtype, "traddr": request.traddr, "gateway_name": request.gateway_name, - "trsvcid": request.trsvcid, "adrfam": request.adrfam, + "trsvcid": request.trsvcid, "adrfam": adrfam, "method": "nvmf_subsystem_add_listener", "req_id": 0} ret = {"code": -errno.EEXIST, "message": f"{request.nqn} already listens on address {request.traddr} port {request.trsvcid}"} msg = "\n".join(["request:", "%s" % json.dumps(req, indent=2), @@ -669,10 +706,10 @@ def create_listener_safe(self, request, context=None): ret = rpc_nvmf.nvmf_subsystem_add_listener( self.spdk_rpc_client, nqn=request.nqn, - trtype=request.trtype, + trtype=trtype, traddr=request.traddr, trsvcid=request.trsvcid, - adrfam=request.adrfam, + adrfam=adrfam, ) self.logger.info(f"create_listener: {ret}") else: @@ -710,10 +747,10 @@ def create_listener_safe(self, request, context=None): self.spdk_rpc_client, nqn=request.nqn, ana_state="inaccessible", - trtype=request.trtype, + trtype=trtype, traddr=request.traddr, trsvcid=request.trsvcid, - adrfam=request.adrfam, + adrfam=adrfam, anagrpid=(x+1) ) except Exception as ex: self.logger.error(f" set_listener_ana_state failed with: \n {ex}") @@ -723,10 +760,10 @@ def create_listener_safe(self, request, context=None): # Update gateway state try: json_req = json_format.MessageToJson( - request, preserving_proto_field_name=True) + request, preserving_proto_field_name=True, including_default_value_fields=True) self.gateway_state.add_listener(request.nqn, request.gateway_name, - request.trtype, request.traddr, + trtype, request.traddr, request.trsvcid, json_req) except Exception as ex: self.logger.error( @@ -743,9 +780,18 @@ def delete_listener_safe(self, request, context=None): ret = True traddr = GatewayConfig.escape_address_if_ipv6(request.traddr) + + trtype = GatewayEnumUtils.get_key_from_value(pb2.TransportType, request.trtype) + if trtype == None: + raise Exception(f"Unknown transport type {request.trtype}") + + adrfam = GatewayEnumUtils.get_key_from_value(pb2.AddressFamily, request.adrfam) + if adrfam == None: + raise Exception(f"Unknown address family {request.adrfam}") + self.logger.info(f"Received request to delete {request.gateway_name}" - f" {request.trtype} listener for {request.nqn} at" - f" {traddr}:{request.trsvcid}., context: {context}") + f" {trtype} listener for {request.nqn} at" + f" {traddr}:{request.trsvcid}, context: {context}") if self.is_discovery_nqn(request.nqn): raise Exception(f"Can't delete a listener from a discovery subsystem") @@ -756,10 +802,10 @@ def delete_listener_safe(self, request, context=None): ret = rpc_nvmf.nvmf_subsystem_remove_listener( self.spdk_rpc_client, nqn=request.nqn, - trtype=request.trtype, + trtype=trtype, traddr=request.traddr, trsvcid=request.trsvcid, - adrfam=request.adrfam, + adrfam=adrfam, ) self.logger.info(f"delete_listener: {ret}") else: @@ -777,7 +823,7 @@ def delete_listener_safe(self, request, context=None): try: self.gateway_state.remove_listener(request.nqn, request.gateway_name, - request.trtype, + trtype, request.traddr, request.trsvcid) except Exception as ex: @@ -806,6 +852,21 @@ def get_subsystems_safe(self, request, context): for s in ret: try: + # Need to adjust values to fit enum constants + try: + listen_addrs = s["listen_addresses"] + except Exception: + listen_addrs = [] + pass + for addr in listen_addrs: + try: + addr["trtype"] = addr["trtype"].upper() + except Exception: + pass + try: + addr["adrfam"] = addr["adrfam"].lower() + except Exception: + pass # Parse the JSON dictionary into the protobuf message subsystem = pb2.subsystem() json_format.Parse(json.dumps(s), subsystem) @@ -849,23 +910,37 @@ def get_spdk_nvmf_log_flags_and_level(self, request, context): def set_spdk_nvmf_logs_safe(self, request, context): """Enables spdk nvmf logs""" self.logger.info(f"Received request to set SPDK nvmf logs") + log_level = None + print_level = None + if request.log_level: + try: + log_level = pb2.LogLevel.keys()[request.log_level] + except Exception: + raise Exception(f"Unknown log level {request.log_level}") + + if request.print_level: + try: + print_level = pb2.LogLevel.keys()[request.print_level] + except Exception: + raise Exception(f"Unknown print level {request.print_level}") + try: nvmf_log_flags = [key for key in rpc_log.log_get_flags(self.spdk_rpc_client).keys() \ if key.startswith('nvmf')] ret = [rpc_log.log_set_flag( self.spdk_rpc_client, flag=flag) for flag in nvmf_log_flags] self.logger.info(f"Set SPDK log flags {nvmf_log_flags} to TRUE") - if request.log_level: - ret_log = rpc_log.log_set_level(self.spdk_rpc_client, level=request.log_level) - self.logger.info(f"Set log level to: {request.log_level}") + if log_level: + ret_log = rpc_log.log_set_level(self.spdk_rpc_client, level=log_level) + self.logger.info(f"Set log level to: {log_level}") ret.append(ret_log) - if request.print_level: + if print_level: ret_print = rpc_log.log_set_print_level( - self.spdk_rpc_client, level=request.print_level) - self.logger.info(f"Set log print level to: {request.print_level}") + self.spdk_rpc_client, level=print_level) + self.logger.info(f"Set log print level to: {print_level}") ret.append(ret_print) except Exception as ex: - self.logger.error(f"set_spdk_nvmf_logs failed with: \n {ex}") + self.logger.error(f"set_spdk_nvmf_logs failed with:\n{ex}") context.set_code(grpc.StatusCode.INTERNAL) context.set_details(f"{ex}") for flag in nvmf_log_flags: diff --git a/control/proto/gateway.proto b/control/proto/gateway.proto index 7a43348a2..05659b535 100644 --- a/control/proto/gateway.proto +++ b/control/proto/gateway.proto @@ -10,6 +10,33 @@ syntax = "proto3"; +enum TransportType { + INVALID = 0; + RDMA = 1; + FC = 2; + TCP = 3; + PCIE = 256; + VFIOUSER = 1024; + CUSTOM = 4096; +} + +enum AddressFamily { + invalid = 0; + ipv4 = 1; + ipv6 = 2; + ib = 3; + fc = 4; +} + +enum LogLevel { + DISABLED = 0; + ERROR = 1; + WARN = 2; + NOTICE = 3; + INFO = 4; + DEBUG = 5; +} + service Gateway { // Creates a bdev from an RBD image rpc create_bdev(create_bdev_req) returns (bdev) {} @@ -117,8 +144,8 @@ message remove_host_req { message create_listener_req { string nqn = 1; string gateway_name = 2; - string trtype = 3; - string adrfam = 4; + TransportType trtype = 3; + AddressFamily adrfam = 4; string traddr = 5; string trsvcid = 6; } @@ -126,8 +153,8 @@ message create_listener_req { message delete_listener_req { string nqn = 1; string gateway_name = 2; - string trtype = 3; - string adrfam = 4; + TransportType trtype = 3; + AddressFamily adrfam = 4; string traddr = 5; string trsvcid = 6; } @@ -142,9 +169,8 @@ message disable_spdk_nvmf_logs_req { } message set_spdk_nvmf_logs_req { - bool flags = 1; - optional string log_level = 2; - optional string print_level = 3; + optional LogLevel log_level = 1; + optional LogLevel print_level = 2; } message get_gateway_info_req { @@ -197,8 +223,8 @@ message gateway_info { message listen_address { string transport = 1; - string trtype = 2; - string adrfam = 3; + TransportType trtype = 2; + AddressFamily adrfam = 3; string traddr = 4; string trsvcid = 5; } diff --git a/control/server.py b/control/server.py index 7675c2e82..2a900775e 100644 --- a/control/server.py +++ b/control/server.py @@ -137,7 +137,7 @@ def _start_discovery_service(self): return try: - rpc_nvmf.nvmf_delete_subsystem(self.spdk_rpc_ping_client, DiscoveryService.DISCOVERY_NQN) + rpc_nvmf.nvmf_delete_subsystem(self.spdk_rpc_ping_client, GatewayConfig.DISCOVERY_NQN) except Exception as ex: self.logger.error(f" Delete Discovery subsystem returned with error: \n {ex}") raise diff --git a/tests/test_cli.py b/tests/test_cli.py index d1ca6b584..140487281 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -21,14 +21,18 @@ nsid = "1" nsid_ipv6 = "3" anagrpid = "2" -trtype = "TCP" gateway_name = socket.gethostname() addr = "127.0.0.1" addr_ipv6 = "::1" server_addr_ipv6 = "2001:db8::3" -listener_list = [["-g", gateway_name, "-a", addr, "-s", "5001"], ["-g", gateway_name, "-a", addr,"-s", "5002"]] +listener_list = [["-g", gateway_name, "-a", addr, "-s", "5001", "-t", "tCp", "-f", "ipV4"], ["-g", gateway_name, "-a", addr, "-s", "5002"]] listener_list_no_port = [["-g", gateway_name, "-a", addr]] -listener_list_ipv6 = [["-g", gateway_name, "-a", addr_ipv6, "-s", "5003"], ["-g", gateway_name, "-a", addr_ipv6, "-s", "5004"]] +listener_list_fc_trtype = [["-g", gateway_name, "-a", addr, "-s", "5010", "--trtype", "FC"]] +listener_list_invalid_trtype = [["-g", gateway_name, "-a", addr, "-s", "5011", "--trtype", "JUNK"]] +listener_list_invalid_adrfam = [["-g", gateway_name, "-a", addr, "-s", "5013", "--adrfam", "JUNK"]] +listener_list_ib_adrfam = [["-g", gateway_name, "-a", addr, "-s", "5014", "--adrfam", "ib"]] +listener_list_ipv6 = [["-g", gateway_name, "-a", addr_ipv6, "-s", "5003", "--adrfam", "ipv6"], ["-g", gateway_name, "-a", addr_ipv6, "-s", "5004", "--adrfam", "IPV6"]] +listener_list_discovery = [["-n", "nqn.2014-08.org.nvmexpress.discovery", "-g", gateway_name, "-a", addr, "-s", "5012"]] config = "ceph-nvmeof.conf" @pytest.fixture(scope="module") @@ -191,15 +195,15 @@ def test_create_listener(self, caplog, listener, gateway): caplog.clear() cli(["create_listener", "-n", subsystem] + listener) assert "enable_ha: False" in caplog.text - assert "ipv4" in caplog.text + assert "ipv4" in caplog.text.lower() assert f"Created {subsystem} listener at {listener[3]}:{listener[5]}: True" in caplog.text @pytest.mark.parametrize("listener_ipv6", listener_list_ipv6) def test_create_listener_ipv6(self, caplog, listener_ipv6, gateway): caplog.clear() - cli(["--server-address", server_addr_ipv6, "create_listener", "-n", subsystem, "--adrfam", "IPV6"] + listener_ipv6) + cli(["--server-address", server_addr_ipv6, "create_listener", "-n", subsystem] + listener_ipv6) assert "enable_ha: False" in caplog.text - assert "IPV6" in caplog.text + assert "ipv6" in caplog.text.lower() assert f"Created {subsystem} listener at [{listener_ipv6[3]}]:{listener_ipv6[5]}: True" in caplog.text @pytest.mark.parametrize("listener", listener_list_no_port) @@ -207,9 +211,79 @@ def test_create_listener_no_port(self, caplog, listener, gateway): caplog.clear() cli(["create_listener", "-n", subsystem] + listener) assert "enable_ha: False" in caplog.text - assert "ipv4" in caplog.text + assert "ipv4" in caplog.text.lower() assert f"Created {subsystem} listener at {listener[3]}:4420: True" in caplog.text + @pytest.mark.parametrize("listener", listener_list_fc_trtype) + def test_create_listener_fc_trtype(self, caplog, listener, gateway): + caplog.clear() + rc = 0 + with pytest.raises(Exception) as ex: + try: + cli(["create_listener", "-n", subsystem] + listener) + except SystemExit as sysex: + rc = sysex + pass + assert "create_listener failed" in str(ex.value) + assert rc != 0 + assert "create_listener failed" in caplog.text + assert "Invalid parameters" in caplog.text + assert '"trtype": "FC"' in caplog.text + + @pytest.mark.parametrize("listener", listener_list_invalid_trtype) + def test_create_listener_invalid_trtype(self, caplog, listener, gateway): + caplog.clear() + with pytest.raises(Exception) as ex: + try: + cli(["create_listener", "-n", subsystem] + listener) + except SystemExit as sysex: + pass + assert "unknown enum label" in str(ex.value) + assert "unknown enum label" in caplog.text + assert f"Created {subsystem} listener at {listener[3]}:{listener[5]}: False" in caplog.text + + @pytest.mark.parametrize("listener", listener_list_invalid_adrfam) + def test_create_listener_invalid_adrfam(self, caplog, listener, gateway): + caplog.clear() + with pytest.raises(Exception) as ex: + try: + cli(["create_listener", "-n", subsystem] + listener) + except SystemExit as sysex: + pass + assert "unknown enum label" in str(ex.value) + assert "unknown enum label" in caplog.text + assert f"Created {subsystem} listener at {listener[3]}:{listener[5]}: False" in caplog.text + + @pytest.mark.parametrize("listener", listener_list_ib_adrfam) + def test_create_listener_ib_adrfam(self, caplog, listener, gateway): + caplog.clear() + rc = 0 + with pytest.raises(Exception) as ex: + try: + cli(["create_listener", "-n", subsystem] + listener) + except SystemExit as sysex: + rc = sysex + pass + assert "create_listener failed" in str(ex.value) + assert rc != 0 + assert "create_listener failed" in caplog.text + assert "Invalid parameters" in caplog.text + assert '"adrfam": "ib"' in caplog.text + + @pytest.mark.parametrize("listener", listener_list_discovery) + def test_create_listener_on_discovery(self, caplog, listener, gateway): + caplog.clear() + rc = 0 + with pytest.raises(Exception) as ex: + try: + cli(["create_listener"] + listener) + except SystemExit as sysex: + rc = sysex + pass + assert "Can't create a listener for a discovery subsystem" in str(ex.value) + assert rc != 0 + assert "Can't create a listener for a discovery subsystem" in caplog.text + class TestDelete: @pytest.mark.parametrize("host", host_list) def test_remove_host(self, caplog, host, gateway): @@ -229,7 +303,7 @@ def test_delete_listener(self, caplog, listener, gateway): @pytest.mark.parametrize("listener_ipv6", listener_list_ipv6) def test_delete_listener_ipv6(self, caplog, listener_ipv6, gateway): caplog.clear() - cli(["--server-address", server_addr_ipv6, "delete_listener", "-n", subsystem, "--adrfam", "IPV6"] + listener_ipv6) + cli(["--server-address", server_addr_ipv6, "delete_listener", "-n", subsystem] + listener_ipv6) assert f"Deleted [{listener_ipv6[3]}]:{listener_ipv6[5]} from {subsystem}: True" in caplog.text @pytest.mark.parametrize("listener", listener_list_no_port) @@ -252,14 +326,16 @@ def test_delete_bdev(self, caplog, gateway): assert "Will remove namespace" not in caplog.text caplog.clear() # Should fail as there is a namespace using the bdev + rc = 0 with pytest.raises(Exception) as ex: try: cli(["delete_bdev", "-b", bdev1]) except SystemExit as sysex: # should fail with non-zero return code - assert sysex != 0 - pass + rc = sysex assert "Device or resource busy" in str(ex.value) + assert rc != 0 + assert "Device or resource busy" in caplog.text assert f"Namespace 2 from {subsystem} is still using bdev {bdev1}" in caplog.text caplog.clear() cli(["delete_bdev", "-b", bdev1, "--force"]) @@ -295,14 +371,16 @@ def test_create_bdev_ana_ipv6(self, caplog, gateway): def test_create_subsystem_ana(self, caplog, gateway): caplog.clear() + rc = 0 with pytest.raises(Exception) as ex: try: cli(["create_subsystem", "-n", subsystem, "-t"]) except SystemExit as sysex: # should fail with non-zero return code - assert sysex != 0 - pass + rc = sysex assert "HA enabled but ANA-reporting is disabled" in str(ex.value) + assert rc != 0 + assert "HA enabled but ANA-reporting is disabled" in caplog.text caplog.clear() cli(["create_subsystem", "-n", subsystem, "-a", "-t"]) assert f"Created subsystem {subsystem}: True" in caplog.text @@ -324,7 +402,7 @@ def test_create_listener_ana(self, caplog, listener, gateway): caplog.clear() cli(["create_listener", "-n", subsystem] + listener) assert "enable_ha: True" in caplog.text - assert "ipv4" in caplog.text + assert "ipv4" in caplog.text.lower() assert f"Created {subsystem} listener at {listener[3]}:{listener[5]}: True" in caplog.text @@ -356,7 +434,7 @@ def test_delete_subsystem_ana(self, caplog, gateway): cli(["delete_subsystem", "-n", subsystem]) assert f"Deleted subsystem {subsystem}: True" in caplog.text -class TestSDKLOg: +class TestSPDKLOg: def test_log_flags(self, caplog, gateway): caplog.clear() cli(["get_spdk_nvmf_log_flags_and_level"]) @@ -365,8 +443,8 @@ def test_log_flags(self, caplog, gateway): assert '"log_level": "NOTICE"' in caplog.text assert '"log_print_level": "INFO"' in caplog.text caplog.clear() - cli(["set_spdk_nvmf_logs", "-f"]) - assert "Set SPDK nvmf logs : True" in caplog.text + cli(["set_spdk_nvmf_logs"]) + assert "Set SPDK nvmf logs: True" in caplog.text caplog.clear() cli(["get_spdk_nvmf_log_flags_and_level"]) assert '"nvmf": true' in caplog.text @@ -374,8 +452,8 @@ def test_log_flags(self, caplog, gateway): assert '"log_level": "NOTICE"' in caplog.text assert '"log_print_level": "INFO"' in caplog.text caplog.clear() - cli(["set_spdk_nvmf_logs", "-f", "-l", "DEBUG"]) - assert "Set SPDK nvmf logs : True" in caplog.text + cli(["set_spdk_nvmf_logs", "-l", "DebuG"]) + assert "Set SPDK nvmf logs: True" in caplog.text caplog.clear() cli(["get_spdk_nvmf_log_flags_and_level"]) assert '"nvmf": true' in caplog.text @@ -383,8 +461,8 @@ def test_log_flags(self, caplog, gateway): assert '"log_level": "DEBUG"' in caplog.text assert '"log_print_level": "INFO"' in caplog.text caplog.clear() - cli(["set_spdk_nvmf_logs", "-f", "-p", "ERROR"]) - assert "Set SPDK nvmf logs : True" in caplog.text + cli(["set_spdk_nvmf_logs", "-p", "eRRor"]) + assert "Set SPDK nvmf logs: True" in caplog.text caplog.clear() cli(["get_spdk_nvmf_log_flags_and_level"]) assert '"nvmf": true' in caplog.text @@ -400,3 +478,11 @@ def test_log_flags(self, caplog, gateway): assert '"nvmf_tcp": false' in caplog.text assert '"log_level": "NOTICE"' in caplog.text assert '"log_print_level": "INFO"' in caplog.text + caplog.clear() + with pytest.raises(Exception) as ex: + try: + cli(["set_spdk_nvmf_logs", "-l", "JUNK"]) + except SystemExit as sysex: + pass + assert "Set SPDK nvmf logs: False" in str(ex.value) + assert "Set SPDK nvmf logs: False" in caplog.text diff --git a/tests/test_multi_gateway.py b/tests/test_multi_gateway.py index e2b42330f..607878b1c 100644 --- a/tests/test_multi_gateway.py +++ b/tests/test_multi_gateway.py @@ -98,7 +98,7 @@ def test_multi_gateway_coordination(config, image, conn): time.sleep(1) listB = json.loads(json_format.MessageToJson( stubB.get_subsystems(get_subsystems_req), - preserving_proto_field_name=True))['subsystems'] + preserving_proto_field_name=True, including_default_value_fields=True))['subsystems'] assert len(listB) == num_subsystems assert listB[num_subsystems-1]["nqn"] == nqn assert listB[num_subsystems-1]["serial_number"] == serial @@ -109,7 +109,7 @@ def test_multi_gateway_coordination(config, image, conn): time.sleep(update_interval_sec + 1) listB = json.loads(json_format.MessageToJson( stubB.get_subsystems(get_subsystems_req), - preserving_proto_field_name=True))['subsystems'] + preserving_proto_field_name=True, including_default_value_fields=True))['subsystems'] assert len(listB) == num_subsystems assert listB[num_subsystems-1]["nqn"] == nqn assert listB[num_subsystems-1]["serial_number"] == serial diff --git a/tests/test_omap_lock.py b/tests/test_omap_lock.py index 4de098aae..e552ca1b7 100644 --- a/tests/test_omap_lock.py +++ b/tests/test_omap_lock.py @@ -224,12 +224,12 @@ def test_multi_gateway_omap_reread(config, conn_omap_reread, caplog): # Until we create some resource on GW-B it shouldn't still have the resrouces created on GW-A, only the discovery subsystem listB = json.loads(json_format.MessageToJson( stubB.get_subsystems(get_subsystems_req), - preserving_proto_field_name=True))['subsystems'] + preserving_proto_field_name=True, including_default_value_fields=True))['subsystems'] assert len(listB) == 1 listA = json.loads(json_format.MessageToJson( stubA.get_subsystems(get_subsystems_req), - preserving_proto_field_name=True))['subsystems'] + preserving_proto_field_name=True, including_default_value_fields=True))['subsystems'] assert len(listA) == num_subsystems bdev2_req = pb2.create_bdev_req(bdev_name=bdev2, @@ -243,7 +243,7 @@ def test_multi_gateway_omap_reread(config, conn_omap_reread, caplog): # Make sure that after reading the OMAP file GW-B has the subsystem and namespace created on GW-A listB = json.loads(json_format.MessageToJson( stubB.get_subsystems(get_subsystems_req), - preserving_proto_field_name=True))['subsystems'] + preserving_proto_field_name=True, including_default_value_fields=True))['subsystems'] assert len(listB) == num_subsystems assert listB[num_subsystems-1]["nqn"] == nqn assert listB[num_subsystems-1]["serial_number"] == serial @@ -303,10 +303,10 @@ def test_multi_gateway_concurrent_changes(config, image, conn_concurrent, caplog get_subsystems_req = pb2.get_subsystems_req() listA = json.loads(json_format.MessageToJson( stubA.get_subsystems(get_subsystems_req), - preserving_proto_field_name=True))['subsystems'] + preserving_proto_field_name=True, including_default_value_fields=True))['subsystems'] listB = json.loads(json_format.MessageToJson( stubB.get_subsystems(get_subsystems_req), - preserving_proto_field_name=True))['subsystems'] + preserving_proto_field_name=True, including_default_value_fields=True))['subsystems'] for i in range(created_resource_count): check_resource_by_index(i, listA) check_resource_by_index(i, listB)