Skip to content

Commit 1b76833

Browse files
avanthakkarnizamial09
authored andcommitted
mgr/dashboard: introduce APIs for NvmeOf management
Fixes: https://tracker.ceph.com/issues/64201 Signed-off-by: avanthakkar <avanjohn@gmail.com>
1 parent 2401531 commit 1b76833

File tree

4 files changed

+188
-2
lines changed

4 files changed

+188
-2
lines changed

src/pybind/mgr/dashboard/controllers/nvmeof.py

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,99 @@
1010

1111
# from ..cephnvmeof.control.cli import GatewayClient
1212

13+
from typing import Optional
1314
from ..security import Scope
1415
from ..services.nvmeof_client import NVMeoFClient
15-
from . import APIDoc, APIRouter, RESTController, Endpoint, ReadPermission, CreatePermission
16+
# from ..services.proto import gateway_pb2 as pb2
17+
from . import APIDoc, APIRouter, RESTController, Endpoint, ReadPermission, CreatePermission, \
18+
DeletePermission, allow_empty_body, UpdatePermission
19+
1620

17-
@APIRouter('/nvmeof', Scope.ISCSI)
21+
@APIRouter('/nvmeof', Scope.NVME_OF)
1822
@APIDoc('NVMe-oF Management API', 'NVMe-oF')
1923
class Nvmeof(RESTController):
2024
@ReadPermission
2125
def list(self):
2226
"""List all NVMeoF gateways"""
2327
return NVMeoFClient().get_subsystems()
28+
29+
30+
@APIRouter('/nvmeof/bdev', Scope.NVME_OF)
31+
@APIDoc('NVMe-oF Block Device Management API', 'NVMe-oF')
32+
class NvmeofBdev(RESTController):
33+
@CreatePermission
34+
def create(self, name: str, rbd_pool: str, rbd_image: str, block_size: int, uuid: Optional[str] = None):
35+
"""Create a new NVMeoF block device"""
36+
return NVMeoFClient().create_bdev(name, rbd_pool, rbd_image, block_size, uuid)
37+
38+
@DeletePermission
39+
@allow_empty_body
40+
def delete(self, name: str, force: bool):
41+
"""Delete an existing NVMeoF block device"""
42+
return NVMeoFClient().delete_bdev(name, force)
43+
44+
@Endpoint('PUT')
45+
@UpdatePermission
46+
@allow_empty_body
47+
def resize(self, name: str, size: int):
48+
"""Resize an existing NVMeoF block device"""
49+
return NVMeoFClient().resize_bdev(name, size)
50+
51+
52+
@APIRouter('/nvmeof/namespace', Scope.NVME_OF)
53+
@APIDoc('NVMe-oF Namespace Management API', 'NVMe-oF')
54+
class NvmeofNamespace(RESTController):
55+
@CreatePermission
56+
def create(self, subsystem_nqn: str, bdev_name: str, nsid: int, anagrpid: Optional[str] = None):
57+
"""Create a new NVMeoF namespace"""
58+
return NVMeoFClient().create_namespace(subsystem_nqn, bdev_name, nsid, anagrpid)
59+
60+
@Endpoint('DELETE', path='{subsystem_nqn}')
61+
def delete(self, subsystem_nqn: str, nsid: int):
62+
"""Delete an existing NVMeoF namespace"""
63+
return NVMeoFClient().delete_namespace(subsystem_nqn, nsid)
64+
65+
@APIRouter('/nvmeof/subsystem', Scope.NVME_OF)
66+
@APIDoc('NVMe-oF Subsystem Management API', 'NVMe-oF')
67+
class NvmeofSubsystem(RESTController):
68+
@CreatePermission
69+
def create(self, subsystem_nqn: str, serial_number: str, max_namespaces: int,
70+
ana_reporting: bool, enable_ha: bool) :
71+
"""Create a new NVMeoF subsystem"""
72+
return NVMeoFClient().create_subsystem(subsystem_nqn, serial_number, max_namespaces,
73+
ana_reporting, enable_ha)
74+
75+
@Endpoint('DELETE', path='{subsystem_nqn}')
76+
def delete(self, subsystem_nqn: str):
77+
"""Delete an existing NVMeoF subsystem"""
78+
return NVMeoFClient().delete_subsystem(subsystem_nqn)
79+
80+
81+
@APIRouter('/nvmeof/hosts', Scope.NVME_OF)
82+
@APIDoc('NVMe-oF Host Management API', 'NVMe-oF')
83+
class NvmeofHost(RESTController):
84+
@CreatePermission
85+
def create(self, subsystem_nqn: str, host_nqn: str):
86+
"""Create a new NVMeoF host"""
87+
return NVMeoFClient().add_host(subsystem_nqn, host_nqn)
88+
89+
@Endpoint('DELETE')
90+
def delete(self, subsystem_nqn: str, host_nqn: str):
91+
"""Delete an existing NVMeoF host"""
92+
return NVMeoFClient().remove_host(subsystem_nqn, host_nqn)
93+
94+
95+
@APIRouter('/nvmeof/listener', Scope.NVME_OF)
96+
@APIDoc('NVMe-oF Listener Management API', 'NVMe-oF')
97+
class NvmeofListener(RESTController):
98+
@CreatePermission
99+
def create(self, nqn: str, gateway: str, trtype: str, adrfam: str,
100+
traddr: str, trsvcid: str):
101+
"""Create a new NVMeoF listener"""
102+
return NVMeoFClient().create_listener(nqn, gateway, trtype, adrfam, traddr, trsvcid)
103+
104+
@Endpoint('DELETE')
105+
def delete(self, nqn: str, gateway: str, trtype, adrfam,
106+
traddr: str, trsvcid: str):
107+
"""Delete an existing NVMeoF listener"""
108+
return NVMeoFClient().delete_listener(nqn, gateway, trtype, adrfam, traddr, trsvcid)

src/pybind/mgr/dashboard/security.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Scope(object):
2626
USER = "user"
2727
DASHBOARD_SETTINGS = "dashboard-settings"
2828
NFS_GANESHA = "nfs-ganesha"
29+
NVME_OF = "nvme-of"
2930

3031
@classmethod
3132
def all_scopes(cls):

src/pybind/mgr/dashboard/services/access_control.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ def from_dict(cls, r_dict):
222222
Scope.ISCSI: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
223223
Scope.RBD_MIRRORING: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
224224
Scope.GRAFANA: [_P.READ],
225+
Scope.NVME_OF: [_P.READ, _P.CREATE, _P.UPDATE, _P.DELETE],
225226
})
226227

227228

src/pybind/mgr/dashboard/services/nvmeof_client.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from enum import Enum
2+
from typing import Optional
13
import grpc
24
import json
35

@@ -9,6 +11,7 @@
911
from google.protobuf.json_format import MessageToJson
1012

1113
from .nvmeof_conf import NvmeofGatewaysConfig
14+
from ..tools import str_to_bool
1215

1316
logger = logging.getLogger('nvmeof_client')
1417

@@ -27,3 +30,99 @@ def __init__(self):
2730
def get_subsystems(self):
2831
response = self.stub.get_subsystems(pb2.get_subsystems_req())
2932
return json.loads(MessageToJson(response))
33+
34+
def create_bdev(self, name: str, rbd_pool: str, rbd_image: str, block_size: int, uuid: Optional[str] = None):
35+
response = self.stub.create_bdev(pb2.create_bdev_req(
36+
bdev_name=name,
37+
rbd_pool_name=rbd_pool,
38+
rbd_image_name=rbd_image,
39+
block_size=block_size,
40+
uuid=uuid
41+
))
42+
return json.loads(MessageToJson(response))
43+
44+
def resize_bdev(self, name: str, size: int):
45+
response = self.stub.resize_bdev(pb2.resize_bdev_req(
46+
bdev_name=name,
47+
new_size=size
48+
))
49+
return json.loads(MessageToJson(response))
50+
51+
def delete_bdev(self, name: str, force: bool):
52+
response = self.stub.delete_bdev(pb2.delete_bdev_req(
53+
bdev_name=name,
54+
force=str_to_bool(force)
55+
))
56+
return json.loads(MessageToJson(response))
57+
58+
def create_subsystem(self, subsystem_nqn: str, serial_number: str, max_namespaces: int,
59+
ana_reporting: bool, enable_ha: bool) :
60+
response = self.stub.create_subsystem(pb2.create_subsystem_req(
61+
subsystem_nqn=subsystem_nqn,
62+
serial_number=serial_number,
63+
max_namespaces=int(max_namespaces),
64+
ana_reporting=str_to_bool(ana_reporting),
65+
enable_ha=str_to_bool(enable_ha)
66+
))
67+
return json.loads(MessageToJson(response))
68+
69+
def delete_subsystem(self, subsystem_nqn: str):
70+
response = self.stub.delete_subsystem(pb2.delete_subsystem_req(
71+
subsystem_nqn=subsystem_nqn
72+
))
73+
return json.loads(MessageToJson(response))
74+
75+
def create_namespace(self, subsystem_nqn: str, bdev_name: str, nsid: int, anagrpid: Optional[str] = None):
76+
response = self.stub.add_namespace(pb2.add_namespace_req(
77+
subsystem_nqn=subsystem_nqn,
78+
bdev_name=bdev_name,
79+
nsid=int(nsid),
80+
anagrpid=anagrpid
81+
))
82+
return json.loads(MessageToJson(response))
83+
84+
def delete_namespace(self, subsystem_nqn: str, nsid: int):
85+
response = self.stub.remove_namespace(pb2.remove_namespace_req(
86+
subsystem_nqn=subsystem_nqn,
87+
nsid=nsid
88+
))
89+
return json.loads(MessageToJson(response))
90+
91+
def add_host(self, subsystem_nqn: str, host_nqn: str):
92+
response = self.stub.add_host(pb2.add_host_req(
93+
subsystem_nqn=subsystem_nqn,
94+
host_nqn=host_nqn
95+
))
96+
return json.loads(MessageToJson(response))
97+
98+
def remove_host(self, subsystem_nqn: str, host_nqn: str):
99+
response = self.stub.remove_host(pb2.remove_host_req(
100+
subsystem_nqn=subsystem_nqn,
101+
host_nqn=host_nqn
102+
))
103+
return json.loads(MessageToJson(response))
104+
105+
def create_listener(self, nqn: str, gateway: str, trtype: str, adrfam: str,
106+
traddr: str, trsvcid: str):
107+
req = pb2.create_listener_req(
108+
nqn=nqn,
109+
gateway_name=gateway,
110+
trtype=pb2.TransportType.Value(trtype.upper()),
111+
adrfam=pb2.AddressFamily.Value(adrfam.lower()),
112+
traddr=traddr,
113+
trsvcid=trsvcid,
114+
)
115+
ret = self.stub.create_listener(req)
116+
return json.loads(MessageToJson(ret))
117+
118+
def delete_listener(self, nqn: str, gateway: str, trttype, adrfam,
119+
traddr: str, trsvcid: str):
120+
response = self.stub.delete_listener(pb2.delete_listener_req(
121+
nqn=nqn,
122+
gateway_name=gateway,
123+
trtype=trttype,
124+
adrfam=adrfam,
125+
traddr=traddr,
126+
trsvcid=trsvcid
127+
))
128+
return json.loads(MessageToJson(response))

0 commit comments

Comments
 (0)