From ae60344eb79ef3c417fa8dc6b7edf914776bad11 Mon Sep 17 00:00:00 2001 From: Konrad Sztyber Date: Fri, 5 Nov 2021 10:23:13 +0100 Subject: [PATCH] sma: volume attach/detach This patch implements the Volume(Attach|Detach) methods. Signed-off-by: Konrad Sztyber Change-Id: I639f1e7b6d4d5a3e52795f9c8b1ae890407e2375 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/10277 Community-CI: Broadcom CI Tested-by: SPDK CI Jenkins Reviewed-by: Ben Walker Reviewed-by: Jim Harris --- python/spdk/sma/device/nvmf_tcp.py | 76 ++++++++++++++++++++++++++++++ python/spdk/sma/sma.py | 28 +++++++++++ 2 files changed, 104 insertions(+) diff --git a/python/spdk/sma/device/nvmf_tcp.py b/python/spdk/sma/device/nvmf_tcp.py index 60e741674..86313a560 100644 --- a/python/spdk/sma/device/nvmf_tcp.py +++ b/python/spdk/sma/device/nvmf_tcp.py @@ -1,5 +1,6 @@ import grpc import logging +import uuid from spdk.rpc.client import JSONRPCException from .device import DeviceManager, DeviceException from ..common import format_volume_id @@ -124,5 +125,80 @@ class NvmfTcpDeviceManager(DeviceManager): else: 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): return handle.startswith('nvmf-tcp') diff --git a/python/spdk/sma/sma.py b/python/spdk/sma/sma.py index 59b314102..9fa921fe1 100644 --- a/python/spdk/sma/sma.py +++ b/python/spdk/sma/sma.py @@ -73,3 +73,31 @@ class StorageManagementAgent(pb2_grpc.StorageManagementAgentServicer): context.set_details('Method is not implemented by selected device type') context.set_code(grpc.StatusCode.UNIMPLEMENTED) 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