Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a command to change namespace visibility #1001

Open
wants to merge 1 commit into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ jobs:
strategy:
fail-fast: false
matrix:
test: ["cli", "cli_change_lb", "cli_change_keys", "state", "multi_gateway", "server", "grpc", "omap_lock", "log_files", "nsid", "psk", "dhchap"]
test: ["cli", "cli_change_lb", "cli_change_keys", "cli_change_ns_visibility", "state", "multi_gateway", "server", "grpc", "omap_lock", "log_files", "nsid", "psk", "dhchap"]
runs-on: ubuntu-latest
env:
HUGEPAGES: 512 # for multi gateway test, approx 256 per gateway instance
Expand Down
96 changes: 82 additions & 14 deletions control/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ def gw_set_log_level(self, args):
gw_actions.append({"name" : "get_log_level", "args" : [], "help" : "Get gateway's log level"})
gw_actions.append({"name" : "set_log_level", "args" : gw_set_log_level_args, "help" : "Set gateway's log level"})
gw_choices = get_actions(gw_actions)

@cli.cmd(gw_actions)
def gw(self, args):
"""Gateway commands"""
Expand Down Expand Up @@ -596,8 +597,6 @@ def spdk_log_level_get(self, args):

def spdk_log_level_set(self, args):
"""Set SPDK log levels and nvmf log flags"""
rc = 0
errmsg = ""

out_func, err_func = self.get_output_functions(args)
log_level = None
Expand Down Expand Up @@ -655,6 +654,7 @@ def spdk_log_level_set(self, args):
spdk_log_actions.append({"name" : "set", "args" : spdk_log_set_args, "help" : "Set SPDK log levels and nvmf log flags"})
spdk_log_actions.append({"name" : "disable", "args" : spdk_log_disable_args, "help" : "Disable SPDK nvmf log flags"})
spdk_log_choices = get_actions(spdk_log_actions)

@cli.cmd(spdk_log_actions)
def spdk_log_level(self, args):
"""SPDK nvmf log level commands"""
Expand Down Expand Up @@ -691,7 +691,7 @@ def subsystem_add(self, args):
new_nqn = ""
try:
new_nqn = ret.nqn
except Exception as ex: # In case of an old gateway the returned value wouldn't have the nqn field
except Exception: # In case of an old gateway the returned value wouldn't have the nqn field
pass
if not new_nqn:
new_nqn = args.subsystem
Expand Down Expand Up @@ -826,7 +826,6 @@ def subsystem_list(self, args):
def subsystem_change_key(self, args):
"""Change subsystem's inband authentication key."""

rc = 0
out_func, err_func = self.get_output_functions(args)

req = pb2.change_subsystem_key_req(subsystem_nqn=args.subsystem, dhchap_key=args.dhchap_key)
Expand Down Expand Up @@ -884,6 +883,7 @@ def subsystem_change_key(self, args):
subsystem_actions.append({"name" : "list", "args" : subsys_list_args, "help" : "List subsystems"})
subsystem_actions.append({"name" : "change_key", "args" : subsys_change_key_args, "help" : "Change subsystem key"})
subsystem_choices = get_actions(subsystem_actions)

@cli.cmd(subsystem_actions)
def subsystem(self, args):
"""Subsystem commands"""
Expand Down Expand Up @@ -1026,11 +1026,11 @@ def listener_list(self, args):
if args.format == "text" or args.format == "plain":
if listeners_info.status == 0:
listeners_list = []
for l in listeners_info.listeners:
adrfam = GatewayEnumUtils.get_key_from_value(pb2.AddressFamily, l.adrfam)
for lstnr in listeners_info.listeners:
adrfam = GatewayEnumUtils.get_key_from_value(pb2.AddressFamily, lstnr.adrfam)
adrfam = self.format_adrfam(adrfam)
secure = "Yes" if l.secure else "No"
listeners_list.append([l.host_name, l.trtype, adrfam, f"{l.traddr}:{l.trsvcid}", secure])
secure = "Yes" if lstnr.secure else "No"
listeners_list.append([lstnr.host_name, lstnr.trtype, adrfam, f"{lstnr.traddr}:{lstnr.trsvcid}", secure])
if len(listeners_list) > 0:
if args.format == "text":
table_format = "fancy_grid"
Expand Down Expand Up @@ -1086,6 +1086,7 @@ def listener_list(self, args):
listener_actions.append({"name" : "del", "args" : listener_del_args, "help" : "Delete a listener"})
listener_actions.append({"name" : "list", "args" : listener_list_args, "help" : "List listeners"})
listener_choices = get_actions(listener_actions)

@cli.cmd(listener_actions)
def listener(self, args):
"""Listener commands"""
Expand Down Expand Up @@ -1216,7 +1217,6 @@ def host_del(self, args):
def host_change_key(self, args):
"""Change host's inband authentication keys."""

rc = 0
out_func, err_func = self.get_output_functions(args)

if args.host_nqn == "*":
Expand Down Expand Up @@ -1327,6 +1327,7 @@ def host_list(self, args):
host_actions.append({"name" : "list", "args" : host_list_args, "help" : "List subsystem's host access"})
host_actions.append({"name" : "change_key", "args" : host_change_key_args, "help" : "Change host's inband authentication keys"})
host_choices = get_actions(host_actions)

@cli.cmd(host_actions)
def host(self, args):
"""Host commands"""
Expand Down Expand Up @@ -1406,6 +1407,7 @@ def connection_list(self, args):
connection_actions = []
connection_actions.append({"name" : "list", "args" : connection_list_args, "help" : "List active connections"})
connection_choices = get_actions(connection_actions)

@cli.cmd(connection_actions)
def connection(self, args):
"""Connection commands"""
Expand Down Expand Up @@ -1601,7 +1603,7 @@ def get_size_in_bytes(self, sz):
try:
sz = sz.strip()
int_size = int(sz)
except:
except Exception:
self.cli.parser.error(f"Size {sz} must be numeric")

int_size *= multiply
Expand Down Expand Up @@ -1638,14 +1640,14 @@ def ns_list(self, args):
lb_group = "<n/a>"
else:
lb_group = str(ns.load_balancing_group)
if ns.no_auto_visible:
if ns.auto_visible:
visibility = "All Hosts"
else:
if len(ns.hosts) > 0:
for hst in ns.hosts:
visibility = break_string(hst, ":", 2) + "\n"
else:
visibility = "Selective"
else:
visibility = "All Hosts"

namespaces_list.append([ns.nsid,
break_string(ns.bdev_name, "-", 2),
Expand Down Expand Up @@ -1977,7 +1979,7 @@ def ns_del_host(self, args):

if args.format == "text" or args.format == "plain":
if ret.status == 0:
out_func(f"Deleting host {args.host_nqn} from namespace {args.nsid} on {args.subsystem}: Successful")
out_func(f"Deleting host {one_host_nqn} from namespace {args.nsid} on {args.subsystem}: Successful")
else:
err_func(f"{ret.error_message}")
elif args.format == "json" or args.format == "yaml":
Expand All @@ -2001,6 +2003,61 @@ def ns_del_host(self, args):

return rc

def ns_change_visibility(self, args):
"""Change namespace visibility."""

out_func, err_func = self.get_output_functions(args)
if args.nsid <= 0:
self.cli.parser.error("nsid value must be positive")

if not args.auto_visible and not args.no_auto_visible:
self.cli.parser.error("Either --auto-visible or --no-auto-visible should be specified")

if args.auto_visible and args.no_auto_visible:
self.cli.parser.error("--auto-visible and --no-auto-visible are mutually exclusive")

if args.auto_visible:
auto_visible = True
elif args.no_auto_visible:
auto_visible = False
else:
assert False

try:
change_visibility_req = pb2.namespace_change_visibility_req(subsystem_nqn=args.subsystem,
nsid=args.nsid, auto_visible=auto_visible,
force=args.force)
ret = self.stub.namespace_change_visibility(change_visibility_req)
except Exception as ex:
ret = pb2.req_status(status = errno.EINVAL, error_message = f"Failure changing namespace visibility:\n{ex}")

if auto_visible:
vis_text = "\"visible to all hosts\""
else:
vis_text = "\"visible to selected hosts\""
if args.format == "text" or args.format == "plain":
if ret.status == 0:
out_func(f"Changing visibility of namespace {args.nsid} in {args.subsystem} to {vis_text}: Successful")
else:
err_func(f"{ret.error_message}")
elif args.format == "json" or args.format == "yaml":
ret_str = json_format.MessageToJson(
ret,
indent=4,
including_default_value_fields=True,
preserving_proto_field_name=True)
if args.format == "json":
out_func(f"{ret_str}")
elif args.format == "yaml":
obj = json.loads(ret_str)
out_func(yaml.dump(obj))
elif args.format == "python":
return ret
else:
assert False

return ret.status

ns_common_args = [
argument("--subsystem", "-n", help="Subsystem NQN", required=True),
]
Expand Down Expand Up @@ -2034,6 +2091,12 @@ def ns_del_host(self, args):
argument("--nsid", help="Namespace ID", type=int, required=True),
argument("--load-balancing-group", "-l", help="Load balancing group", type=int, required=True),
]
ns_change_visibility_args_list = ns_common_args + [
argument("--nsid", help="Namespace ID", type=int, required=True),
argument("--auto-visible", help="Visible to all hosts", action='store_true', required=False),
argument("--no-auto-visible", help="Visible to selected hosts only", action='store_true', required=False),
argument("--force", help="Change visibility of namespace even if there hosts added to it or active connections on the subsystem", action='store_true', required=False),
]
ns_set_qos_args_list = ns_common_args + [
argument("--nsid", help="Namespace ID", type=int, required=True),
argument("--rw-ios-per-second", help="R/W IOs per second limit, 0 means unlimited", type=int),
Expand All @@ -2059,7 +2122,9 @@ def ns_del_host(self, args):
ns_actions.append({"name" : "set_qos", "args" : ns_set_qos_args_list, "help" : "Set QOS limits for a namespace"})
ns_actions.append({"name" : "add_host", "args" : ns_add_host_args_list, "help" : "Add a host to a namespace"})
ns_actions.append({"name" : "del_host", "args" : ns_del_host_args_list, "help" : "Delete a host from a namespace"})
ns_actions.append({"name" : "change_visibility", "args" : ns_change_visibility_args_list, "help" : "Change visibility for a namespace"})
ns_choices = get_actions(ns_actions)

@cli.cmd(ns_actions, ["ns"])
def namespace(self, args):
"""Namespace commands"""
Expand All @@ -2081,6 +2146,8 @@ def namespace(self, args):
return self.ns_add_host(args)
elif args.action == "del_host":
return self.ns_del_host(args)
elif args.action == "change_visibility":
return self.ns_change_visibility(args)
if not args.action:
self.cli.parser.error(f"missing action for namespace command (choose from {GatewayClient.ns_choices})")

Expand Down Expand Up @@ -2135,5 +2202,6 @@ def main(args=None) -> int:

return main_common(client, parsed_args)


if __name__ == "__main__":
sys.exit(main())
Loading
Loading