Skip to content

Commit

Permalink
Don't call get_subsystems from within delete_bdev().
Browse files Browse the repository at this point in the history
Fixes #260

Signed-off-by: Gil Bregman <[email protected]>
  • Loading branch information
gbregman committed Oct 18, 2023
1 parent 2b7b62d commit 14e0aab
Showing 1 changed file with 75 additions and 76 deletions.
151 changes: 75 additions & 76 deletions control/grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import logging
import os
import threading
import errno

import spdk.rpc.bdev as rpc_bdev
import spdk.rpc.nvmf as rpc_nvmf
Expand Down Expand Up @@ -155,8 +156,7 @@ def create_bdev_safe(self, request, context=None):
if context:
# Update gateway state
try:
json_req = json_format.MessageToJson(
request, preserving_proto_field_name=True)
json_req = json_format.MessageToJson(request, preserving_proto_field_name=True)
self.gateway_state.add_bdev(bdev_name, json_req)
except Exception as ex:
self.logger.error(
Expand All @@ -169,63 +169,80 @@ def create_bdev(self, request, context=None):
with self.rpc_lock:
return self.create_bdev_safe(request, context)

def find_bdev_namespaces(self, bdev_name):
ns_list = []
local_state_dict = self.gateway_state.local.get_state()
local_state_keys = local_state_dict.keys()
for key, val in local_state_dict.items():
if key.startswith(self.gateway_state.local.NAMESPACE_PREFIX):
try:
req = json_format.Parse(val, pb2.add_namespace_req(), ignore_unknown_fields = True)
ns_bdev_name = req.bdev_name
if ns_bdev_name == bdev_name:
nsid = req.nsid
nqn = req.subsystem_nqn
ns_list.insert(0, {"nqn" : nqn, "nsid" : nsid})
except Exception as ex:
self.logger.error(f"Got exception trying to get bdev {bdev_name} namespaces: {ex}")
pass

return ns_list

def delete_bdev_handle_exception(self, context, ex):
self.logger.error(f"delete_bdev failed with: \n {ex}")
if context:
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details(f"{ex}")
return pb2.req_status()

def delete_bdev_safe(self, request, context=None):
"""Deletes a bdev."""

self.logger.info(f"Received request to delete bdev {request.bdev_name}")
use_excep = None
req_get_subsystems = pb2.get_subsystems_req()
# We already hold the lock, so call the safe version, do not try lock again
ret = self.get_subsystems_safe(req_get_subsystems, context)
subsystems = json.loads(ret.subsystems)
for subsystem in subsystems:
for namespace in subsystem['namespaces']:
if namespace['bdev_name'] == request.bdev_name:
# We found a namespace still using this bdev. If --force was used we will try to remove this namespace.
# Otherwise fail with EBUSY
if request.force:
self.logger.info(f"Will remove namespace {namespace['nsid']} from {subsystem['nqn']} as it is using bdev {request.bdev_name}")
try:
req_rm_ns = pb2.remove_namespace_req(subsystem_nqn=subsystem['nqn'], nsid=namespace['nsid'])
# We already hold the lock, so call the safe version, do not try lock again
ret = self.remove_namespace_safe(req_rm_ns, context)
self.logger.info(
f"Removed namespace {namespace['nsid']} from {subsystem['nqn']}: {ret.status}")
except Exception as ex:
self.logger.error(f"Error removing namespace {namespace['nsid']} from {subsystem['nqn']}, will delete bdev {request.bdev_name} anyway: {ex}")
pass
else:
self.logger.error(f"Namespace {namespace['nsid']} from {subsystem['nqn']} is still using bdev {request.bdev_name}. You need to either remove it or use the '--force' command line option")
req = {"name": request.bdev_name, "method": "bdev_rbd_delete", "req_id": 0}
ret = {"code": -16, "message": "Device or resource busy"}
msg = "\n".join(["request:", "%s" % json.dumps(req, indent=2),
"Got JSON-RPC error response",
"response:",
json.dumps(ret, indent=2)])
use_excep = Exception(msg)
ns_list = self.find_bdev_namespaces(request.bdev_name)
for namespace in ns_list:
# We found a namespace still using this bdev. If --force was used we will try to remove this namespace.
# Otherwise fail with EBUSY
try:
ns_nsid = namespace["nsid"]
ns_nqn = namespace["nqn"]
except Exception as ex:
self.logger.error(f"Got exception while trying to remove namespace: {namespace} which stil uses bdev {request.bdev_name}: {ex}")
continue

if request.force:
self.logger.info(f"Will remove namespace {ns_nsid} from {ns_nqn} as it is using bdev {request.bdev_name}")
try:
req_rm_ns = pb2.remove_namespace_req(subsystem_nqn=ns_nqn, nsid=ns_nsid)
# We already hold the lock, so call the safe version, do not try lock again
ret = self.remove_namespace_safe(req_rm_ns, context)
self.logger.info(f"Removed namespace {ns_nsid} from {ns_nqn}: {ret.status}")
except Exception as ex:
self.logger.error(f"Error removing namespace {ns_nsid} from {ns_nqn}, will delete bdev {request.bdev_name} anyway: {ex}")
pass
else:
self.logger.error(f"Namespace {ns_nsid} from {ns_nqn} is still using bdev {request.bdev_name}. You need to either remove it or use the '--force' command line option")
req = {"name": request.bdev_name, "method": "bdev_rbd_delete", "req_id": 0}
ret = {"code": -errno.EBUSY, "message": os.strerror(errno.EBUSY)}
msg = "\n".join(["request:", "%s" % json.dumps(req, indent=2),
"Got JSON-RPC error response", "response:", json.dumps(ret, indent=2)])
return self.delete_bdev_handle_exception(context, Exception(msg))

try:
if use_excep:
raise use_excep
ret = rpc_bdev.bdev_rbd_delete(
self.spdk_rpc_client,
request.bdev_name,
)
self.logger.info(f"delete_bdev {request.bdev_name}: {ret}")
except Exception as ex:
self.logger.error(f"delete_bdev failed with: \n {ex}")
if context:
context.set_code(grpc.StatusCode.INTERNAL)
context.set_details(f"{ex}")
return pb2.req_status()
return self.delete_bdev_handle_exception(context, ex)

if context:
# Update gateway state
try:
self.gateway_state.remove_bdev(request.bdev_name)
except Exception as ex:
self.logger.error(
f"Error persisting delete_bdev {request.bdev_name}: {ex}")
self.logger.error(f"Error persisting delete_bdev {request.bdev_name}: {ex}")
raise

return pb2.req_status(status=ret)
Expand All @@ -237,8 +254,7 @@ def delete_bdev(self, request, context=None):
def create_subsystem_safe(self, request, context=None):
"""Creates a subsystem."""

self.logger.info(
f"Received request to create subsystem {request.subsystem_nqn}")
self.logger.info(f"Received request to create subsystem {request.subsystem_nqn}")
min_cntlid = self.config.getint_with_default("gateway", "min_controller_id", 1)
max_cntlid = self.config.getint_with_default("gateway", "max_controller_id", 65519)
if not request.serial_number:
Expand All @@ -265,13 +281,10 @@ def create_subsystem_safe(self, request, context=None):
if context:
# Update gateway state
try:
json_req = json_format.MessageToJson(
request, preserving_proto_field_name=True)
self.gateway_state.add_subsystem(request.subsystem_nqn,
json_req)
json_req = json_format.MessageToJson(request, preserving_proto_field_name=True)
self.gateway_state.add_subsystem(request.subsystem_nqn, json_req)
except Exception as ex:
self.logger.error(f"Error persisting create_subsystem"
f" {request.subsystem_nqn}: {ex}")
self.logger.error(f"Error persisting create_subsystem {request.subsystem_nqn}: {ex}")
raise

return pb2.req_status(status=ret)
Expand All @@ -283,8 +296,7 @@ def create_subsystem(self, request, context=None):
def delete_subsystem_safe(self, request, context=None):
"""Deletes a subsystem."""

self.logger.info(
f"Received request to delete subsystem {request.subsystem_nqn}")
self.logger.info(f"Received request to delete subsystem {request.subsystem_nqn}")
try:
ret = rpc_nvmf.nvmf_delete_subsystem(
self.spdk_rpc_client,
Expand All @@ -303,8 +315,7 @@ def delete_subsystem_safe(self, request, context=None):
try:
self.gateway_state.remove_subsystem(request.subsystem_nqn)
except Exception as ex:
self.logger.error(f"Error persisting delete_subsystem"
f" {request.subsystem_nqn}: {ex}")
self.logger.error(f"Error persisting delete_subsystem {request.subsystem_nqn}: {ex}")
raise

return pb2.req_status(status=ret)
Expand All @@ -316,8 +327,7 @@ def delete_subsystem(self, request, context=None):
def add_namespace_safe(self, request, context=None):
"""Adds a namespace to a subsystem."""

self.logger.info(f"Received request to add {request.bdev_name} to"
f" {request.subsystem_nqn}")
self.logger.info(f"Received request to add {request.bdev_name} to {request.subsystem_nqn}")
try:
nsid = rpc_nvmf.nvmf_subsystem_add_ns(
self.spdk_rpc_client,
Expand All @@ -340,11 +350,9 @@ def add_namespace_safe(self, request, context=None):
request.nsid = nsid
json_req = json_format.MessageToJson(
request, preserving_proto_field_name=True)
self.gateway_state.add_namespace(request.subsystem_nqn,
str(nsid), json_req)
self.gateway_state.add_namespace(request.subsystem_nqn, str(nsid), json_req)
except Exception as ex:
self.logger.error(
f"Error persisting add_namespace {nsid}: {ex}")
self.logger.error(f"Error persisting add_namespace {nsid}: {ex}")
raise

return pb2.nsid(nsid=nsid, status=True)
Expand All @@ -356,8 +364,7 @@ def add_namespace(self, request, context=None):
def remove_namespace_safe(self, request, context=None):
"""Removes a namespace from a subsystem."""

self.logger.info(f"Received request to remove {request.nsid} from"
f" {request.subsystem_nqn}")
self.logger.info(f"Received request to remove {request.nsid} from {request.subsystem_nqn}")
try:
ret = rpc_nvmf.nvmf_subsystem_remove_ns(
self.spdk_rpc_client,
Expand All @@ -378,8 +385,7 @@ def remove_namespace_safe(self, request, context=None):
self.gateway_state.remove_namespace(request.subsystem_nqn,
str(request.nsid))
except Exception as ex:
self.logger.error(
f"Error persisting remove_namespace {request.nsid}: {ex}")
self.logger.error(f"Error persisting remove_namespace {request.nsid}: {ex}")
raise

return pb2.req_status(status=ret)
Expand Down Expand Up @@ -426,8 +432,7 @@ def add_host_safe(self, request, context=None):
self.gateway_state.add_host(request.subsystem_nqn,
request.host_nqn, json_req)
except Exception as ex:
self.logger.error(
f"Error persisting add_host {request.host_nqn}: {ex}")
self.logger.error(f"Error persisting add_host {request.host_nqn}: {ex}")
raise

return pb2.req_status(status=ret)
Expand All @@ -441,9 +446,7 @@ def remove_host_safe(self, request, context=None):

try:
if request.host_nqn == "*": # Disable allow any host access
self.logger.info(
f"Received request to disable any host access to"
f" {request.subsystem_nqn}")
self.logger.info(f"Received request to disable any host access to {request.subsystem_nqn}")
ret = rpc_nvmf.nvmf_subsystem_allow_any_host(
self.spdk_rpc_client,
nqn=request.subsystem_nqn,
Expand Down Expand Up @@ -501,8 +504,7 @@ def create_listener_safe(self, request, context=None):
)
self.logger.info(f"create_listener: {ret}")
else:
raise Exception(f"Gateway name must match current gateway"
f" ({self.gateway_name})")
raise Exception(f"Gateway name must match current gateway ({self.gateway_name})")
except Exception as ex:
self.logger.error(f"create_listener failed with: \n {ex}")
if context:
Expand All @@ -520,8 +522,7 @@ def create_listener_safe(self, request, context=None):
request.trtype, request.traddr,
request.trsvcid, json_req)
except Exception as ex:
self.logger.error(
f"Error persisting add_listener {request.trsvcid}: {ex}")
self.logger.error(f"Error persisting add_listener {request.trsvcid}: {ex}")
raise

return pb2.req_status(status=ret)
Expand Down Expand Up @@ -549,8 +550,7 @@ def delete_listener_safe(self, request, context=None):
)
self.logger.info(f"delete_listener: {ret}")
else:
raise Exception(f"Gateway name must match current gateway"
f" ({self.gateway_name})")
raise Exception(f"Gateway name must match current gateway ({self.gateway_name})")
except Exception as ex:
self.logger.error(f"delete_listener failed with: \n {ex}")
if context:
Expand All @@ -567,8 +567,7 @@ def delete_listener_safe(self, request, context=None):
request.traddr,
request.trsvcid)
except Exception as ex:
self.logger.error(
f"Error persisting delete_listener {request.trsvcid}: {ex}")
self.logger.error(f"Error persisting delete_listener {request.trsvcid}: {ex}")
raise

return pb2.req_status(status=ret)
Expand Down

0 comments on commit 14e0aab

Please sign in to comment.