]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: introduce APIs for NvmeOf management
authoravanthakkar <avanjohn@gmail.com>
Mon, 11 Dec 2023 13:20:54 +0000 (18:50 +0530)
committerNizamudeen A <nia@redhat.com>
Tue, 30 Jan 2024 10:09:35 +0000 (15:39 +0530)
Fixes: https://tracker.ceph.com/issues/64201
Signed-off-by: avanthakkar <avanjohn@gmail.com>
src/pybind/mgr/dashboard/controllers/nvmeof.py
src/pybind/mgr/dashboard/security.py
src/pybind/mgr/dashboard/services/access_control.py
src/pybind/mgr/dashboard/services/nvmeof_client.py

index 274985245fe3b4672aabe4268b56d91402f25fc9..6458c47a885677227dc326e0be73cb83a9f014f9 100644 (file)
 
 # from ..cephnvmeof.control.cli import GatewayClient
 
+from typing import Optional
 from ..security import Scope
 from ..services.nvmeof_client import NVMeoFClient
-from . import APIDoc, APIRouter, RESTController, Endpoint, ReadPermission, CreatePermission
+# from ..services.proto import gateway_pb2 as pb2
+from . import APIDoc, APIRouter, RESTController, Endpoint, ReadPermission, CreatePermission, \
+    DeletePermission, allow_empty_body, UpdatePermission
+    
 
-@APIRouter('/nvmeof', Scope.ISCSI)
+@APIRouter('/nvmeof', Scope.NVME_OF)
 @APIDoc('NVMe-oF Management API', 'NVMe-oF')
 class Nvmeof(RESTController):
     @ReadPermission
     def list(self):
         """List all NVMeoF gateways"""
         return NVMeoFClient().get_subsystems()
+
+
+@APIRouter('/nvmeof/bdev', Scope.NVME_OF)
+@APIDoc('NVMe-oF Block Device Management API', 'NVMe-oF')
+class NvmeofBdev(RESTController):
+    @CreatePermission
+    def create(self, name: str, rbd_pool: str, rbd_image: str, block_size: int, uuid: Optional[str] = None):
+        """Create a new NVMeoF block device"""
+        return NVMeoFClient().create_bdev(name, rbd_pool, rbd_image, block_size, uuid)
+    
+    @DeletePermission
+    @allow_empty_body
+    def delete(self, name: str, force: bool):
+        """Delete an existing NVMeoF block device"""
+        return NVMeoFClient().delete_bdev(name, force)
+    
+    @Endpoint('PUT')
+    @UpdatePermission
+    @allow_empty_body
+    def resize(self, name: str, size: int):
+        """Resize an existing NVMeoF block device"""
+        return NVMeoFClient().resize_bdev(name, size)
+
+
+@APIRouter('/nvmeof/namespace', Scope.NVME_OF)
+@APIDoc('NVMe-oF Namespace Management API', 'NVMe-oF')
+class NvmeofNamespace(RESTController):
+    @CreatePermission
+    def create(self, subsystem_nqn: str, bdev_name: str, nsid: int, anagrpid: Optional[str] = None):
+        """Create a new NVMeoF namespace"""
+        return NVMeoFClient().create_namespace(subsystem_nqn, bdev_name, nsid, anagrpid)
+    
+    @Endpoint('DELETE', path='{subsystem_nqn}')
+    def delete(self, subsystem_nqn: str, nsid: int):
+        """Delete an existing NVMeoF namespace"""
+        return NVMeoFClient().delete_namespace(subsystem_nqn, nsid)
+    
+@APIRouter('/nvmeof/subsystem', Scope.NVME_OF)
+@APIDoc('NVMe-oF Subsystem Management API', 'NVMe-oF')
+class NvmeofSubsystem(RESTController):
+    @CreatePermission
+    def create(self, subsystem_nqn: str, serial_number: str, max_namespaces: int,
+                         ana_reporting: bool, enable_ha: bool) :
+        """Create a new NVMeoF subsystem"""
+        return NVMeoFClient().create_subsystem(subsystem_nqn, serial_number, max_namespaces,
+                                               ana_reporting, enable_ha)
+    
+    @Endpoint('DELETE', path='{subsystem_nqn}')
+    def delete(self, subsystem_nqn: str):
+        """Delete an existing NVMeoF subsystem"""
+        return NVMeoFClient().delete_subsystem(subsystem_nqn)
+
+
+@APIRouter('/nvmeof/hosts', Scope.NVME_OF)
+@APIDoc('NVMe-oF Host Management API', 'NVMe-oF')
+class NvmeofHost(RESTController):
+    @CreatePermission
+    def create(self, subsystem_nqn: str, host_nqn: str):
+        """Create a new NVMeoF host"""
+        return NVMeoFClient().add_host(subsystem_nqn, host_nqn)
+    
+    @Endpoint('DELETE')
+    def delete(self, subsystem_nqn: str, host_nqn: str):
+        """Delete an existing NVMeoF host"""
+        return NVMeoFClient().remove_host(subsystem_nqn, host_nqn)
+
+
+@APIRouter('/nvmeof/listener', Scope.NVME_OF)
+@APIDoc('NVMe-oF Listener Management API', 'NVMe-oF')
+class NvmeofListener(RESTController):
+    @CreatePermission
+    def create(self, nqn: str, gateway: str, trtype: str, adrfam: str,
+               traddr: str, trsvcid: str):
+        """Create a new NVMeoF listener"""
+        return NVMeoFClient().create_listener(nqn, gateway, trtype, adrfam, traddr, trsvcid)
+    
+    @Endpoint('DELETE')
+    def delete(self, nqn: str, gateway: str, trtype, adrfam,
+               traddr: str, trsvcid: str):
+        """Delete an existing NVMeoF listener"""
+        return NVMeoFClient().delete_listener(nqn, gateway, trtype, adrfam, traddr, trsvcid)
\ No newline at end of file
index 4c6e5c564af3f17b14001b95df51f351b1f6939f..2b624aabcc725ffbaf43ed207cbf836ddce0c946 100644 (file)
@@ -26,6 +26,7 @@ class Scope(object):
     USER = "user"
     DASHBOARD_SETTINGS = "dashboard-settings"
     NFS_GANESHA = "nfs-ganesha"
+    NVME_OF = "nvme-of"
 
     @classmethod
     def all_scopes(cls):
index 0cbe49bb160ad5359dc6fd47c7f7dd244fe850cc..b45f81fb9b1ddfa16e73072923b29ecc086c6370 100644 (file)
@@ -222,6 +222,7 @@ BLOCK_MGR_ROLE = Role(
         Scope.ISCSI: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
         Scope.RBD_MIRRORING: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
         Scope.GRAFANA: [_P.READ],
+        Scope.NVME_OF: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
     })
 
 
index ec8911abc20b3e7705d898ad1839eac21ac17570..ff7c6dfc8ced32112b4e5c725e92df16dfe03dfe 100644 (file)
@@ -1,3 +1,5 @@
+from enum import Enum
+from typing import Optional
 import grpc
 import json
 
@@ -9,6 +11,7 @@ from .proto import gateway_pb2_grpc as pb2_grpc
 from google.protobuf.json_format import MessageToJson
 
 from .nvmeof_conf import NvmeofGatewaysConfig
+from ..tools import str_to_bool
 
 logger = logging.getLogger('nvmeof_client')
 
@@ -27,3 +30,99 @@ class NVMeoFClient(object):
     def get_subsystems(self):
         response = self.stub.get_subsystems(pb2.get_subsystems_req())
         return json.loads(MessageToJson(response))
+    
+    def create_bdev(self, name: str, rbd_pool: str, rbd_image: str, block_size: int, uuid: Optional[str] = None):
+        response = self.stub.create_bdev(pb2.create_bdev_req(
+            bdev_name=name,
+            rbd_pool_name=rbd_pool,
+            rbd_image_name=rbd_image,
+            block_size=block_size,
+            uuid=uuid
+        ))
+        return json.loads(MessageToJson(response))
+    
+    def resize_bdev(self, name: str, size: int):
+        response = self.stub.resize_bdev(pb2.resize_bdev_req(
+            bdev_name=name,
+            new_size=size
+        ))
+        return json.loads(MessageToJson(response))
+    
+    def delete_bdev(self, name: str, force: bool):
+        response = self.stub.delete_bdev(pb2.delete_bdev_req(
+            bdev_name=name,
+            force=str_to_bool(force)
+        ))
+        return json.loads(MessageToJson(response))
+    
+    def create_subsystem(self, subsystem_nqn: str, serial_number: str, max_namespaces: int,
+                         ana_reporting: bool, enable_ha: bool) :
+        response = self.stub.create_subsystem(pb2.create_subsystem_req(
+            subsystem_nqn=subsystem_nqn,
+            serial_number=serial_number,
+            max_namespaces=int(max_namespaces),
+            ana_reporting=str_to_bool(ana_reporting),
+            enable_ha=str_to_bool(enable_ha)
+        ))
+        return json.loads(MessageToJson(response))
+    
+    def delete_subsystem(self, subsystem_nqn: str):
+        response = self.stub.delete_subsystem(pb2.delete_subsystem_req(
+            subsystem_nqn=subsystem_nqn
+        ))
+        return json.loads(MessageToJson(response))
+    
+    def create_namespace(self, subsystem_nqn: str, bdev_name: str, nsid: int, anagrpid: Optional[str] = None):
+        response = self.stub.add_namespace(pb2.add_namespace_req(
+            subsystem_nqn=subsystem_nqn,
+            bdev_name=bdev_name,
+            nsid=int(nsid),
+            anagrpid=anagrpid
+        ))
+        return json.loads(MessageToJson(response))
+    
+    def delete_namespace(self, subsystem_nqn: str, nsid: int):
+        response = self.stub.remove_namespace(pb2.remove_namespace_req(
+            subsystem_nqn=subsystem_nqn,
+            nsid=nsid
+        ))
+        return json.loads(MessageToJson(response))
+    
+    def add_host(self, subsystem_nqn: str, host_nqn: str):
+        response = self.stub.add_host(pb2.add_host_req(
+            subsystem_nqn=subsystem_nqn,
+            host_nqn=host_nqn
+        ))
+        return json.loads(MessageToJson(response))
+    
+    def remove_host(self, subsystem_nqn: str, host_nqn: str):
+        response = self.stub.remove_host(pb2.remove_host_req(
+            subsystem_nqn=subsystem_nqn,
+            host_nqn=host_nqn
+        ))
+        return json.loads(MessageToJson(response))
+
+    def create_listener(self, nqn: str, gateway: str, trtype: str, adrfam: str,
+                        traddr: str, trsvcid: str):
+        req = pb2.create_listener_req(
+                nqn=nqn,
+                gateway_name=gateway,
+                trtype=pb2.TransportType.Value(trtype.upper()),
+                adrfam=pb2.AddressFamily.Value(adrfam.lower()),
+                traddr=traddr,
+                trsvcid=trsvcid,
+            )
+        ret = self.stub.create_listener(req)
+        return json.loads(MessageToJson(ret))
+    
+    def delete_listener(self, nqn: str, gateway: str, trttype, adrfam,
+                        traddr: str, trsvcid: str):
+        response = self.stub.delete_listener(pb2.delete_listener_req(
+            nqn=nqn,
+            gateway_name=gateway,
+            trtype=trttype,
+            adrfam=adrfam,
+            traddr=traddr,
+            trsvcid=trsvcid
+        ))
+        return json.loads(MessageToJson(response))