sma: volume attach/detach

This patch implements the Volume(Attach|Detach) methods.

Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: I639f1e7b6d4d5a3e52795f9c8b1ae890407e2375
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/10277
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Konrad Sztyber 2021-11-05 10:23:13 +01:00 committed by Tomasz Zawadzki
parent 2ea8747935
commit ae60344eb7
2 changed files with 104 additions and 0 deletions

View File

@ -1,5 +1,6 @@
import grpc import grpc
import logging import logging
import uuid
from spdk.rpc.client import JSONRPCException from spdk.rpc.client import JSONRPCException
from .device import DeviceManager, DeviceException from .device import DeviceManager, DeviceException
from ..common import format_volume_id from ..common import format_volume_id
@ -124,5 +125,80 @@ class NvmfTcpDeviceManager(DeviceManager):
else: else:
logging.info(f'Tried to delete a non-existing device: {nqn}') logging.info(f'Tried to delete a non-existing device: {nqn}')
def _find_bdev(self, client, guid):
try:
return client.call('bdev_get_bdevs', {'name': guid})[0]
except JSONRPCException:
return None
@_check_transport
def attach_volume(self, request):
nqn = self._get_nqn_from_handle(request.device_handle)
volume_id = format_volume_id(request.volume.volume_id)
if volume_id is None:
raise DeviceException(grpc.StatusCode.INVALID_ARGUMENT,
'Invalid volume ID')
try:
with self._client() as client:
bdev = self._find_bdev(client, volume_id)
if bdev is None:
raise DeviceException(grpc.StatusCode.NOT_FOUND,
'Invalid volume GUID')
subsystems = client.call('nvmf_get_subsystems')
for subsys in subsystems:
if subsys['nqn'] == nqn:
break
else:
raise DeviceException(grpc.StatusCode.NOT_FOUND,
'Invalid device handle')
if bdev['name'] not in [ns['name'] for ns in subsys['namespaces']]:
result = client.call('nvmf_subsystem_add_ns',
{'nqn': nqn,
'namespace': {
'bdev_name': bdev['name']}})
if not result:
raise DeviceException(grpc.StatusCode.INTERNAL,
'Failed to attach volume')
except JSONRPCException:
raise DeviceException(grpc.StatusCode.INTERNAL,
'Failed to attach volume')
@_check_transport
def detach_volume(self, request):
nqn = self._get_nqn_from_handle(request.device_handle)
volume = format_volume_id(request.volume_id)
if volume is None:
raise DeviceException(grpc.StatusCode.INVALID_ARGUMENT,
'Invalid volume ID')
try:
with self._client() as client:
bdev = self._find_bdev(client, volume)
if bdev is None:
logging.info(f'Tried to detach non-existing volume: {volume}')
return
subsystems = client.call('nvmf_get_subsystems')
for subsys in subsystems:
if subsys['nqn'] == nqn:
break
else:
logging.info(f'Tried to detach volume: {volume} from non-existing ' +
f'device: {nqn}')
return
for ns in subsys['namespaces']:
if ns['name'] != bdev['name']:
continue
result = client.call('nvmf_subsystem_remove_ns',
{'nqn': nqn,
'nsid': ns['nsid']})
if not result:
raise DeviceException(grpc.StatusCode.INTERNAL,
'Failed to detach volume')
break
except JSONRPCException:
raise DeviceException(grpc.StatusCode.INTERNAL,
'Failed to detach volume')
def owns_device(self, handle): def owns_device(self, handle):
return handle.startswith('nvmf-tcp') return handle.startswith('nvmf-tcp')

View File

@ -73,3 +73,31 @@ class StorageManagementAgent(pb2_grpc.StorageManagementAgentServicer):
context.set_details('Method is not implemented by selected device type') context.set_details('Method is not implemented by selected device type')
context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_code(grpc.StatusCode.UNIMPLEMENTED)
return response return response
@_grpc_method
def AttachVolume(self, request, context):
response = pb2.AttachVolumeResponse()
try:
device = self._find_device_by_handle(request.device_handle)
if device is None:
raise DeviceException(grpc.StatusCode.NOT_FOUND, 'Invalid device handle')
device.attach_volume(request)
except DeviceException as ex:
context.set_details(ex.message)
context.set_code(ex.code)
except NotImplementedError:
context.set_details('Method is not implemented by selected device type')
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
return response
@_grpc_method
def DetachVolume(self, request, context):
response = pb2.DetachVolumeResponse()
try:
device = self._find_device_by_handle(request.device_handle)
if device is not None:
device.detach_volume(request)
except DeviceException as ex:
context.set_details(ex.message)
context.set_code(ex.code)
return response