Commit ae60344e authored by Konrad Sztyber's avatar Konrad Sztyber Committed by Tomasz Zawadzki
Browse files

sma: volume attach/detach



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

Signed-off-by: default avatarKonrad 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: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
parent 2ea87479
Loading
Loading
Loading
Loading
+76 −0
Original line number Diff line number Diff line
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')
+28 −0
Original line number Diff line number Diff line
@@ -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