]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: fix NVMeoF API 55685/head
authorErnesto Puerta <epuertat@redhat.com>
Wed, 13 Mar 2024 13:06:10 +0000 (14:06 +0100)
committerNizamudeen A <nia@redhat.com>
Mon, 25 Mar 2024 05:05:03 +0000 (10:35 +0530)
* Update NVMe-oF gRPC Proto to 1.0.0
* Error handling,
* Missing PATCH for certain namespace ops (resize, set QoS, set balance
  groups),
* Stop bypassing gRPC payloads and validate those in the back-end,
* Fix incorrect HTTP 1.1 semantics for some POST/DELETE and URIs.
* Catch errors/exceptions.
* Clean-up EndpointDoc Params
* Run Black linter.
* Remove most of NVMeoFClient glue code between gRPC and controller.
* Fix namespace delete endpoint by exposing trsvcid
* nvmeof io_stats support

Fixes: https://tracker.ceph.com/issues/64890
Signed-off-by: Ernesto Puerta <epuertat@redhat.com>
(cherry picked from commit 4adf48998d97f8199bcb86c8d8994bb5989c198a)

14 files changed:
src/pybind/mgr/dashboard/controllers/__init__.py
src/pybind/mgr/dashboard/controllers/_docs.py
src/pybind/mgr/dashboard/controllers/_endpoint.py
src/pybind/mgr/dashboard/controllers/_rest_controller.py
src/pybind/mgr/dashboard/controllers/auth.py
src/pybind/mgr/dashboard/controllers/docs.py
src/pybind/mgr/dashboard/controllers/nvmeof.py
src/pybind/mgr/dashboard/model/nvmeof.py [new file with mode: 0644]
src/pybind/mgr/dashboard/openapi.yaml
src/pybind/mgr/dashboard/requirements.txt
src/pybind/mgr/dashboard/services/exception.py
src/pybind/mgr/dashboard/services/nvmeof_client.py
src/pybind/mgr/dashboard/services/proto/gateway.proto
src/pybind/mgr/dashboard/services/proto/gateway_pb2.py

index af3f276ebfa2faf9c8306e52a2091e013a5a4586..3db5da5d323590eb064d5e46793dd4e84f24055d 100755 (executable)
@@ -2,7 +2,7 @@ from ._api_router import APIRouter
 from ._auth import ControllerAuthMixin
 from ._base_controller import BaseController
 from ._crud import CRUDCollectionMethod, CRUDEndpoint, CRUDResourceMethod, SecretStr
-from ._docs import APIDoc, EndpointDoc
+from ._docs import APIDoc, EndpointDoc, Param
 from ._endpoint import Endpoint, Proxy
 from ._helpers import ENDPOINT_MAP, allow_empty_body, \
     generate_controller_routes, json_error_page, validate_ceph_type
@@ -23,6 +23,7 @@ __all__ = [
     'Task',
     'ControllerAuthMixin',
     'EndpointDoc',
+    'Param',
     'APIDoc',
     'allow_empty_body',
     'ENDPOINT_MAP',
index 5bd7a5a7a6ea5c6fd45173ea877347aed027596e..7301875f6b4f83d56d92cb6aa4681c4428dad3f0 100644 (file)
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Optional, Tuple, Union
+from typing import Any, Dict, List, NamedTuple, Optional, Tuple, Type, Union
 
 from ..api.doc import SchemaInput, SchemaType
 
@@ -115,6 +115,13 @@ class EndpointDoc:  # noqa: N802
         return func
 
 
+class Param(NamedTuple):
+    type: Union[Type, List[Type]]
+    description: str
+    optional: bool = False
+    default: Optional[Any] = None
+
+
 class APIDoc(object):
     def __init__(self, description="", group=""):
         self.tag = group
index fccab89c34979a9d02273d64598f1e208e0e6ecb..6d77dcdcea4aa3b961a3179efbe2a1fb601edaa1 100644 (file)
@@ -12,9 +12,9 @@ class Endpoint:
         if method is None:
             method = 'GET'
         elif not isinstance(method, str) or \
-                method.upper() not in ['GET', 'POST', 'DELETE', 'PUT']:
+                method.upper() not in ['GET', 'POST', 'DELETE', 'PUT', 'PATCH']:
             raise TypeError("Possible values for method are: 'GET', 'POST', "
-                            "'DELETE', or 'PUT'")
+                            "'DELETE', 'PUT', 'PATCH'")
 
         method = method.upper()
 
@@ -25,7 +25,7 @@ class Endpoint:
                                 " path parameters by default".format(method))
 
         if path_params is None:
-            if method in ['POST', 'PUT']:
+            if method in ['POST', 'PUT', 'PATCH']:
                 path_params = []
 
         if query_params is None:
@@ -41,7 +41,7 @@ class Endpoint:
         self.version = version
 
     def __call__(self, func):
-        if self.method in ['POST', 'PUT']:
+        if self.method in ['POST', 'PUT', 'PATCH']:
             func_params = _get_function_params(func)
             for param in func_params:
                 if param['name'] in self.path_params and not param['required']:
index 0224c366f3bcae58fa133dc6899e0a5541f84a67..c3bd07cf807b7281c466be8af825c80ce1b69316 100644 (file)
@@ -49,6 +49,7 @@ class RESTController(BaseController, skip_registry=True):
         'GET': Permission.READ,
         'POST': Permission.CREATE,
         'PUT': Permission.UPDATE,
+        'PATCH': Permission.UPDATE,
         'DELETE': Permission.DELETE
     }
 
@@ -60,7 +61,8 @@ class RESTController(BaseController, skip_registry=True):
         ('get', {'method': 'GET', 'resource': True, 'status': 200, 'version': APIVersion.DEFAULT}),
         ('delete', {'method': 'DELETE', 'resource': True, 'status': 204, 'version': APIVersion.DEFAULT}),  # noqa E501 #pylint: disable=line-too-long
         ('set', {'method': 'PUT', 'resource': True, 'status': 200, 'version': APIVersion.DEFAULT}),
-        ('singleton_set', {'method': 'PUT', 'resource': False, 'status': 200, 'version': APIVersion.DEFAULT})  # noqa E501 #pylint: disable=line-too-long
+        ('singleton_set', {'method': 'PUT', 'resource': False, 'status': 200, 'version': APIVersion.DEFAULT}),  # noqa E501 #pylint: disable=line-too-long
+        ('update', {'method': 'PATCH', 'resource': True, 'status': 200, 'version': APIVersion.DEFAULT})  # noqa E501 #pylint: disable=line-too-long
     ])
 
     @classmethod
index 196f027b293ee5ecaa29a863777d7cb326b0c552..d60fb5ca730e39424366bbd9cabef588a32fcef6 100644 (file)
@@ -34,6 +34,7 @@ class Auth(RESTController, ControllerAuthMixin):
     """
     Provide authenticates and returns JWT token.
     """
+    # pylint: disable=R0912
 
     def create(self, username, password):
         user_data = AuthManager.authenticate(username, password)
index 2ade4ef9bad4c8c6f392715c46b002f069b34e7f..9ecb513621a104ddf5a4212bc141eabfbb35cb0b 100644 (file)
@@ -215,7 +215,7 @@ class Docs(BaseController):
             resp['201'] = {'description': "Resource created.",
                            'content': {version.to_mime_type():
                                        {'type': 'object'}}}
-        if method.lower() == 'put':
+        if method.lower() in ['put', 'patch']:
             resp['200'] = {'description': "Resource updated.",
                            'content': {version.to_mime_type():
                                        {'type': 'object'}}}
@@ -315,7 +315,7 @@ class Docs(BaseController):
     @classmethod
     def gen_paths(cls, all_endpoints):
         # pylint: disable=R0912
-        method_order = ['get', 'post', 'put', 'delete']
+        method_order = ['get', 'post', 'put', 'patch', 'delete']
         paths = {}
         for path, endpoints in sorted(list(ENDPOINT_MAP.items()),
                                       key=lambda p: p[0]):
@@ -344,7 +344,7 @@ class Docs(BaseController):
                 if summary:
                     methods[method.lower()]['summary'] = summary
 
-                if method.lower() in ['post', 'put']:
+                if method.lower() in ['post', 'put', 'patch']:
                     cls.set_request_body_param(endpoint.body_params, method, methods, p_info)
                     cls.set_request_body_param(endpoint.query_params, method, methods, p_info)
 
index b485a5aa17b22cedff3157a396c88844f1aa969f..a3b4925b0eeee8e50ca0c14a8cb377c18321bb74 100644 (file)
 # -*- coding: utf-8 -*-
-import json
 from typing import Optional
 
+from ..model import nvmeof as model
 from ..security import Scope
-from . import APIDoc, APIRouter, CreatePermission, DeletePermission, Endpoint, \
-    EndpointDoc, ReadPermission, RESTController
+from ..tools import str_to_bool
+from . import APIDoc, APIRouter, Endpoint, EndpointDoc, Param, ReadPermission, RESTController
 
 try:
-    from google.protobuf.json_format import MessageToJson
-
-    from ..services.nvmeof_client import NVMeoFClient
+    from ..services.nvmeof_client import NVMeoFClient, empty_response, \
+        handle_nvmeof_error, map_collection, map_model
 except ImportError:
-    MessageToJson = None
+    pass
 else:
-    @APIRouter('/nvmeof/namespace', Scope.NVME_OF)
-    @APIDoc('NVMe-oF Namespace Management API', 'NVMe-oF')
-    class NvmeofNamespace(RESTController):
-        @ReadPermission
-        @EndpointDoc('List all NVMeoF namespaces',
-                     parameters={
-                         'subsystem_nqn': (str, 'NVMeoF subsystem NQN')
-                     })
-        def list(self, subsystem_nqn: str):
-            response = MessageToJson(NVMeoFClient().list_namespaces(subsystem_nqn))
-            return json.loads(response)
-
-        @CreatePermission
-        @EndpointDoc('Create a new NVMeoF namespace',
-                     parameters={
-                         'rbd_pool': (str, 'RBD pool name'),
-                         'rbd_image': (str, 'RBD image name'),
-                         'subsystem_nqn': (str, 'NVMeoF subsystem NQN'),
-                         'create_image': (bool, 'Create RBD image'),
-                         'image_size': (int, 'RBD image size'),
-                         'block_size': (int, 'NVMeoF namespace block size')
-                     })
-        def create(self, rbd_pool: str, rbd_image: str, subsystem_nqn: str,
-                   create_image: Optional[bool] = True, image_size: Optional[int] = 1024,
-                   block_size: int = 512):
-            response = NVMeoFClient().create_namespace(rbd_pool, rbd_image,
-                                                       subsystem_nqn, block_size,
-                                                       create_image, image_size)
-            return json.loads(MessageToJson(response))
-
-        @Endpoint('DELETE', path='{subsystem_nqn}')
-        @EndpointDoc('Delete an existing NVMeoF namespace',
-                     parameters={
-                         'subsystem_nqn': (str, 'NVMeoF subsystem NQN')
-                     })
-        @DeletePermission
-        def delete(self, subsystem_nqn: str):
-            response = NVMeoFClient().delete_namespace(subsystem_nqn)
-            return json.loads(MessageToJson(response))
-
-    @APIRouter('/nvmeof/subsystem', Scope.NVME_OF)
-    @APIDoc('NVMe-oF Subsystem Management API', 'NVMe-oF')
-    class NvmeofSubsystem(RESTController):
-        @ReadPermission
-        @EndpointDoc("List all NVMeoF subsystems",
-                     parameters={
-                         'subsystem_nqn': (str, 'NVMeoF subsystem NQN'),
-                     })
-        @ReadPermission
-        def list(self, subsystem_nqn: Optional[str] = None):
-            response = MessageToJson(NVMeoFClient().list_subsystems(
-                subsystem_nqn=subsystem_nqn))
-
-            return json.loads(response)
-
-        @CreatePermission
-        @EndpointDoc('Create a new NVMeoF subsystem',
-                     parameters={
-                         'subsystem_nqn': (str, 'NVMeoF subsystem NQN'),
-                         'serial_number': (str, 'NVMeoF subsystem serial number'),
-                         'max_namespaces': (int, 'Maximum number of namespaces')
-                     })
-        def create(self, subsystem_nqn: str):
-            response = NVMeoFClient().create_subsystem(subsystem_nqn)
-            return json.loads(MessageToJson(response))
-
-        @DeletePermission
-        @Endpoint('DELETE', path='{subsystem_nqn}')
-        @EndpointDoc('Delete an existing NVMeoF subsystem',
-                     parameters={
-                         'subsystem_nqn': (str, 'NVMeoF subsystem NQN'),
-                         'force': (bool, 'Force delete')
-                     })
-        def delete(self, subsystem_nqn: str, force: Optional[bool] = False):
-            response = NVMeoFClient().delete_subsystem(subsystem_nqn, force)
-            return json.loads(MessageToJson(response))
-
-    @APIRouter('/nvmeof/hosts', Scope.NVME_OF)
-    @APIDoc('NVMe-oF Host Management API', 'NVMe-oF')
-    class NvmeofHost(RESTController):
-        @ReadPermission
-        @EndpointDoc('List all allowed hosts for an NVMeoF subsystem',
-                     parameters={
-                         'subsystem_nqn': (str, 'NVMeoF subsystem NQN')
-                     })
-        def list(self, subsystem_nqn: str):
-            response = MessageToJson(NVMeoFClient().list_hosts(subsystem_nqn))
-            return json.loads(response)
-
-        @CreatePermission
-        @EndpointDoc('Allow hosts to access an NVMeoF subsystem',
-                     parameters={
-                         'subsystem_nqn': (str, 'NVMeoF subsystem NQN'),
-                         'host_nqn': (str, 'NVMeoF host NQN')
-                     })
-        def create(self, subsystem_nqn: str, host_nqn: str):
-            response = NVMeoFClient().add_host(subsystem_nqn, host_nqn)
-            return json.loads(MessageToJson(response))
-
-        @DeletePermission
-        @EndpointDoc('Disallow hosts from accessing an NVMeoF subsystem',
-                     parameters={
-                         'subsystem_nqn': (str, 'NVMeoF subsystem NQN'),
-                         'host_nqn': (str, 'NVMeoF host NQN')
-                     })
-        def delete(self, subsystem_nqn: str, host_nqn: str):
-            response = NVMeoFClient().remove_host(subsystem_nqn, host_nqn)
-            return json.loads(MessageToJson(response))
-
-    @APIRouter('/nvmeof/listener', Scope.NVME_OF)
-    @APIDoc('NVMe-oF Listener Management API', 'NVMe-oF')
-    class NvmeofListener(RESTController):
-        @ReadPermission
-        @EndpointDoc('List all NVMeoF listeners',
-                     parameters={
-                         'subsystem_nqn': (str, 'NVMeoF subsystem NQN')
-                     })
-        def list(self, subsystem_nqn: str):
-            response = MessageToJson(NVMeoFClient().list_listeners(subsystem_nqn))
-            return json.loads(response)
-
-        @CreatePermission
-        @EndpointDoc('Create a new NVMeoF listener',
-                     parameters={
-                         'nqn': (str, 'NVMeoF subsystem NQN'),
-                         'gateway': (str, 'NVMeoF gateway'),
-                         'traddr': (str, 'NVMeoF transport address')
-                     })
-        def create(self, nqn: str, gateway: str, traddr: Optional[str] = None):
-            response = NVMeoFClient().create_listener(nqn, gateway, traddr)
-            return json.loads(MessageToJson(response))
-
-        @DeletePermission
-        @EndpointDoc('Delete an existing NVMeoF listener',
-                     parameters={
-                         'nqn': (str, 'NVMeoF subsystem NQN'),
-                         'gateway': (str, 'NVMeoF gateway'),
-                         'traddr': (str, 'NVMeoF transport address')
-                     })
-        def delete(self, nqn: str, gateway: str, traddr: Optional[str] = None):
-            response = NVMeoFClient().delete_listener(nqn, gateway, traddr)
-            return json.loads(MessageToJson(response))
-
-    @APIRouter('/nvmeof/gateway', Scope.NVME_OF)
-    @APIDoc('NVMe-oF Gateway Management API', 'NVMe-oF')
-    class NvmeofGateway(RESTController):
+    @APIRouter("/nvmeof/gateway", Scope.NVME_OF)
+    @APIDoc("NVMe-oF Gateway Management API", "NVMe-oF Gateway")
+    class NVMeoFGateway(RESTController):
+        @EndpointDoc("Get information about the NVMeoF gateway")
+        @map_model(model.GatewayInfo)
+        @handle_nvmeof_error
+        def list(self):
+            return NVMeoFClient().stub.get_gateway_info(
+                NVMeoFClient.pb2.get_gateway_info_req()
+            )
+
+    @APIRouter("/nvmeof/subsystem", Scope.NVME_OF)
+    @APIDoc("NVMe-oF Subsystem Management API", "NVMe-oF Subsystem")
+    class NVMeoFSubsystem(RESTController):
+        @EndpointDoc("List all NVMeoF subsystems")
+        @map_collection(model.Subsystem, pick="subsystems")
+        @handle_nvmeof_error
+        def list(self):
+            return NVMeoFClient().stub.list_subsystems(
+                NVMeoFClient.pb2.list_subsystems_req()
+            )
+
+        @EndpointDoc(
+            "Get information from a specific NVMeoF subsystem",
+            parameters={"nqn": Param(str, "NVMeoF subsystem NQN")},
+        )
+        @map_model(model.Subsystem, first="subsystems")
+        @handle_nvmeof_error
+        def get(self, nqn: str):
+            return NVMeoFClient().stub.list_subsystems(
+                NVMeoFClient.pb2.list_subsystems_req(subsystem_nqn=nqn)
+            )
+
+        @EndpointDoc(
+            "Create a new NVMeoF subsystem",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "max_namespaces": Param(int, "Maximum number of namespaces", True, 256),
+                "enable_ha": Param(bool, "Enable high availability"),
+            },
+        )
+        @empty_response
+        @handle_nvmeof_error
+        def create(self, nqn: str, enable_ha: bool, max_namespaces: int = 256):
+            return NVMeoFClient().stub.create_subsystem(
+                NVMeoFClient.pb2.create_subsystem_req(
+                    subsystem_nqn=nqn, max_namespaces=max_namespaces, enable_ha=enable_ha
+                )
+            )
+
+        @EndpointDoc(
+            "Delete an existing NVMeoF subsystem",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "force": Param(bool, "Force delete", "false"),
+            },
+        )
+        @empty_response
+        @handle_nvmeof_error
+        def delete(self, nqn: str, force: Optional[str] = "false"):
+            return NVMeoFClient().stub.delete_subsystem(
+                NVMeoFClient.pb2.delete_subsystem_req(
+                    subsystem_nqn=nqn, force=str_to_bool(force)
+                )
+            )
+
+    @APIRouter("/nvmeof/subsystem/{nqn}/listener", Scope.NVME_OF)
+    @APIDoc("NVMe-oF Subsystem Listener Management API", "NVMe-oF Subsystem Listener")
+    class NVMeoFListener(RESTController):
+        @EndpointDoc(
+            "List all NVMeoF listeners",
+            parameters={"nqn": Param(str, "NVMeoF subsystem NQN")},
+        )
+        @map_collection(model.Listener, pick="listeners")
+        @handle_nvmeof_error
+        def list(self, nqn: str):
+            return NVMeoFClient().stub.list_listeners(
+                NVMeoFClient.pb2.list_listeners_req(subsystem=nqn)
+            )
+
+        @EndpointDoc(
+            "Create a new NVMeoF listener",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "gateway": Param(str, "NVMeoF gateway"),
+                "traddr": Param(str, "NVMeoF transport address"),
+                "trsvcid": Param(int, "NVMeoF transport service port"),
+                "adrfam": Param(str, "NVMeoF address family"),
+                "trtype": Param(str, "NVMeoF transport type"),
+            },
+        )
+        @empty_response
+        @handle_nvmeof_error
+        def create(
+            self,
+            nqn: str,
+            gateway: str,
+            traddr: str,
+            trsvcid: Optional[int] = 4420,
+            adrfam: Optional[str] = "ipv4",
+        ):
+            return NVMeoFClient().stub.create_listener(
+                NVMeoFClient.pb2.create_listener_req(
+                    nqn=nqn,
+                    gateway_name=gateway,
+                    traddr=traddr,
+                    trsvcid=trsvcid,
+                    adrfam=adrfam,
+                )
+            )
+
+        @EndpointDoc(
+            "Delete an existing NVMeoF listener",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "gateway": Param(str, "NVMeoF gateway"),
+                "traddr": Param(str, "NVMeoF transport address"),
+                "trsvid": Param(int, "NVMeoF transport service port"),
+            },
+        )
+        @empty_response
+        @handle_nvmeof_error
+        def delete(self, nqn: str, gateway: str, traddr: Optional[str] = None,
+                   trsvcid: Optional[int] = 4420):
+            return NVMeoFClient().stub.delete_listener(
+                NVMeoFClient.pb2.delete_listener_req(
+                    nqn=nqn, gateway_name=gateway, traddr=traddr, trsvcid=int(trsvcid)
+                )
+            )
+
+    @APIRouter("/nvmeof/subsystem/{nqn}/namespace", Scope.NVME_OF)
+    @APIDoc("NVMe-oF Subsystem Namespace Management API", "NVMe-oF Subsystem Namespace")
+    class NVMeoFNamespace(RESTController):
+        @EndpointDoc(
+            "List all NVMeoF namespaces in a subsystem",
+            parameters={"nqn": Param(str, "NVMeoF subsystem NQN")},
+        )
+        @map_collection(model.Namespace, pick="namespaces")
+        @handle_nvmeof_error
+        def list(self, nqn: str):
+            return NVMeoFClient().stub.list_namespaces(
+                NVMeoFClient.pb2.list_namespaces_req(subsystem=nqn)
+            )
+
+        @EndpointDoc(
+            "Get info from specified NVMeoF namespace",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "nsid": Param(str, "NVMeoF Namespace ID"),
+            },
+        )
+        @map_model(model.Namespace, first="namespaces")
+        @handle_nvmeof_error
+        def get(self, nqn: str, nsid: str):
+            return NVMeoFClient().stub.list_namespaces(
+                NVMeoFClient.pb2.list_namespaces_req(subsystem=nqn, nsid=int(nsid))
+            )
+
         @ReadPermission
-        @Endpoint()
-        @EndpointDoc('List all NVMeoF gateways')
-        def info(self):
-            response = MessageToJson(NVMeoFClient().gateway_info())
-            return json.loads(response)
+        @Endpoint('GET', '{nsid}/io_stats')
+        @EndpointDoc(
+            "Get IO stats from specified NVMeoF namespace",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "nsid": Param(str, "NVMeoF Namespace ID"),
+            },
+        )
+        @map_model(model.NamespaceIOStats)
+        @handle_nvmeof_error
+        def io_stats(self, nqn: str, nsid: str):
+            return NVMeoFClient().stub.namespace_get_io_stats(
+                NVMeoFClient.pb2.namespace_get_io_stats_req(
+                    subsystem_nqn=nqn, nsid=int(nsid))
+            )
+
+        @EndpointDoc(
+            "Create a new NVMeoF namespace",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "rbd_pool": Param(str, "RBD pool name"),
+                "rbd_image_name": Param(str, "RBD image name"),
+                "create_image": Param(bool, "Create RBD image"),
+                "size": Param(int, "RBD image size"),
+                "block_size": Param(int, "NVMeoF namespace block size"),
+                "load_balancing_group": Param(int, "Load balancing group"),
+            },
+        )
+        @map_model(model.NamespaceCreation)
+        @handle_nvmeof_error
+        def create(
+            self,
+            nqn: str,
+            rbd_image_name: str,
+            rbd_pool: str = "rbd",
+            create_image: Optional[bool] = True,
+            size: Optional[int] = 1024,
+            block_size: int = 512,
+            load_balancing_group: Optional[int] = None,
+        ):
+            return NVMeoFClient().stub.namespace_add(
+                NVMeoFClient.pb2.namespace_add_req(
+                    subsystem_nqn=nqn,
+                    rbd_image_name=rbd_image_name,
+                    rbd_pool_name=rbd_pool,
+                    block_size=block_size,
+                    create_image=create_image,
+                    size=size,
+                    anagrpid=load_balancing_group,
+                )
+            )
+
+        @EndpointDoc(
+            "Update an existing NVMeoF namespace",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "nsid": Param(str, "NVMeoF Namespace ID"),
+                "rbd_image_size": Param(int, "RBD image size"),
+                "load_balancing_group": Param(int, "Load balancing group"),
+                "rw_ios_per_second": Param(int, "Read/Write IOPS"),
+                "rw_mbytes_per_second": Param(int, "Read/Write MB/s"),
+                "r_mbytes_per_second": Param(int, "Read MB/s"),
+                "w_mbytes_per_second": Param(int, "Write MB/s"),
+            },
+        )
+        @empty_response
+        @handle_nvmeof_error
+        def update(
+            self,
+            nqn: str,
+            nsid: str,
+            rbd_image_size: Optional[int] = None,
+            load_balancing_group: Optional[int] = None,
+            rw_ios_per_second: Optional[int] = None,
+            rw_mbytes_per_second: Optional[int] = None,
+            r_mbytes_per_second: Optional[int] = None,
+            w_mbytes_per_second: Optional[int] = None,
+        ):
+            if rbd_image_size:
+                mib = 1024 * 1024
+                new_size_mib = int((rbd_image_size + mib - 1) / mib)
+
+                response = NVMeoFClient().stub.namespace_resize(
+                    NVMeoFClient.pb2.namespace_resize_req(
+                        subsystem_nqn=nqn, nsid=int(nsid), new_size=new_size_mib
+                    )
+                )
+                if response.status != 0:
+                    return response
+
+            if load_balancing_group:
+                response = NVMeoFClient().stub.namespace_change_load_balancing_group(
+                    NVMeoFClient.pb2.namespace_change_load_balancing_group_req(
+                        subsystem_nqn=nqn, nsid=int(nsid), anagrpid=load_balancing_group
+                    )
+                )
+                if response.status != 0:
+                    return response
+
+            if (
+                rw_ios_per_second
+                or rw_mbytes_per_second
+                or r_mbytes_per_second
+                or w_mbytes_per_second
+            ):
+                response = NVMeoFClient().stub.namespace_set_qos_limits(
+                    NVMeoFClient.pb2.namespace_set_qos_req(
+                        subsystem_nqn=nqn,
+                        nsid=int(nsid),
+                        rw_ios_per_second=rw_ios_per_second,
+                        rw_mbytes_per_second=rw_mbytes_per_second,
+                        r_mbytes_per_second=r_mbytes_per_second,
+                        w_mbytes_per_second=w_mbytes_per_second,
+                    )
+                )
+                if response.status != 0:
+                    return response
+
+            return response
+
+        @EndpointDoc(
+            "Delete an existing NVMeoF namespace",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "nsid": Param(str, "NVMeoF Namespace ID"),
+            },
+        )
+        @empty_response
+        @handle_nvmeof_error
+        def delete(self, nqn: str, nsid: str):
+            return NVMeoFClient().stub.namespace_delete(
+                NVMeoFClient.pb2.namespace_delete_req(subsystem_nqn=nqn, nsid=int(nsid))
+            )
+
+    @APIRouter("/nvmeof/subsystem/{nqn}/host", Scope.NVME_OF)
+    @APIDoc("NVMe-oF Subsystem Host Allowlist Management API",
+            "NVMe-oF Subsystem Host Allowlist")
+    class NVMeoFHost(RESTController):
+        @EndpointDoc(
+            "List all allowed hosts for an NVMeoF subsystem",
+            parameters={"nqn": Param(str, "NVMeoF subsystem NQN")},
+        )
+        @map_collection(
+            model.Host,
+            pick="hosts",
+            # Display the "allow any host" option as another host item
+            finalize=lambda i, o: [model.Host(nqn="*")._asdict()] + o
+            if i.allow_any_host
+            else o,
+        )
+        @handle_nvmeof_error
+        def list(self, nqn: str):
+            return NVMeoFClient().stub.list_hosts(
+                NVMeoFClient.pb2.list_hosts_req(subsystem=nqn)
+            )
+
+        @EndpointDoc(
+            "Allow hosts to access an NVMeoF subsystem",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "host_nqn": Param(str, 'NVMeoF host NQN. Use "*" to allow any host.'),
+            },
+        )
+        @empty_response
+        @handle_nvmeof_error
+        def create(self, nqn: str, host_nqn: str):
+            return NVMeoFClient().stub.add_host(
+                NVMeoFClient.pb2.add_host_req(subsystem_nqn=nqn, host_nqn=host_nqn)
+            )
+
+        @EndpointDoc(
+            "Disallow hosts from accessing an NVMeoF subsystem",
+            parameters={
+                "nqn": Param(str, "NVMeoF subsystem NQN"),
+                "host_nqn": Param(str, 'NVMeoF host NQN. Use "*" to disallow any host.'),
+            },
+        )
+        @empty_response
+        @handle_nvmeof_error
+        def delete(self, nqn: str, host_nqn: str):
+            return NVMeoFClient().stub.remove_host(
+                NVMeoFClient.pb2.remove_host_req(subsystem_nqn=nqn, host_nqn=host_nqn)
+            )
+
+    @APIRouter("/nvmeof/subsystem/{nqn}/connection", Scope.NVME_OF)
+    @APIDoc("NVMe-oF Subsystem Connection Management API", "NVMe-oF Subsystem Connection")
+    class NVMeoFConnection(RESTController):
+        @EndpointDoc(
+            "List all NVMeoF Subsystem Connections",
+            parameters={"nqn": Param(str, "NVMeoF subsystem NQN")},
+        )
+        @map_collection(model.Connection, pick="connections")
+        @handle_nvmeof_error
+        def list(self, nqn: str):
+            return NVMeoFClient().stub.list_connections(
+                NVMeoFClient.pb2.list_connections_req(subsystem=nqn)
+            )
diff --git a/src/pybind/mgr/dashboard/model/nvmeof.py b/src/pybind/mgr/dashboard/model/nvmeof.py
new file mode 100644 (file)
index 0000000..543ae6a
--- /dev/null
@@ -0,0 +1,92 @@
+from typing import NamedTuple, Optional
+
+
+class GatewayInfo(NamedTuple):
+    cli_version: str
+    version: str
+    name: str
+    group: str
+    addr: str
+    port: int
+    load_balancing_group: int
+    spdk_version: Optional[str] = ""
+
+
+class Subsystem(NamedTuple):
+    nqn: str
+    enable_ha: bool
+    serial_number: str
+    model_number: str
+    min_cntlid: int
+    max_cntlid: int
+    namespace_count: int
+    subtype: str
+    max_namespaces: int
+
+
+class Connection(NamedTuple):
+    traddr: str
+    trsvcid: int
+    trtype: str
+    adrfam: int
+    connected: bool
+    qpairs_count: int
+    controller_id: int
+
+
+class NamespaceCreation(NamedTuple):
+    nsid: int
+
+
+class Namespace(NamedTuple):
+    nsid: Optional[int]
+    uuid: Optional[str]
+    bdev_name: str
+    rbd_image_name: str
+    rbd_pool_name: str
+    load_balancing_group: int
+    rbd_image_size: int
+    block_size: int
+    rw_ios_per_second: int
+    rw_mbytes_per_second: int
+    r_mbytes_per_second: int
+    w_mbytes_per_second: int
+
+
+class NamespaceIOStats(NamedTuple):
+    nsid: int
+    uuid: str
+    bdev_name: str
+    tick_rate: int
+    ticks: int
+    bytes_read: int
+    num_read_ops: int
+    bytes_written: int
+    num_write_ops: int
+    bytes_unmapped: int
+    num_unmap_ops: int
+    read_latency_ticks: int
+    max_read_latency_ticks: int
+    min_read_latency_ticks: int
+    write_latency_ticks: int
+    max_write_latency_ticks: int
+    min_write_latency_ticks: int
+    unmap_latency_ticks: int
+    max_unmap_latency_ticks: int
+    min_unmap_latency_ticks: int
+    copy_latency_ticks: int
+    max_copy_latency_ticks: int
+    min_copy_latency_ticks: int
+    # io_error: List[int]
+
+
+class Listener(NamedTuple):
+    gateway_name: str
+    trtype: str
+    traddr: str
+    adrfam: Optional[str] = "ipv4"
+    trsvcid: Optional[int] = 4420
+
+
+class Host(NamedTuple):
+    nqn: str
index 23dbb5d7d9348815bd198854d03fcdbcc5480c90..6546982dfd892402bc42826d4ec0578a6ed90183 100644 (file)
@@ -7696,7 +7696,7 @@ paths:
       summary: Updates an NFS-Ganesha export
       tags:
       - NFS-Ganesha
-  /api/nvmeof/gateway/info:
+  /api/nvmeof/gateway:
     get:
       parameters: []
       responses:
@@ -7716,10 +7716,32 @@ paths:
             trace.
       security:
       - jwt: []
-      summary: List all NVMeoF gateways
+      summary: Get information about the NVMeoF gateway
       tags:
-      - NVMe-oF
-  /api/nvmeof/hosts:
+      - NVMe-oF Gateway
+  /api/nvmeof/subsystem:
+    get:
+      parameters: []
+      responses:
+        '200':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: OK
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      summary: List all NVMeoF subsystems
+      tags:
+      - NVMe-oF Subsystem
     post:
       parameters: []
       requestBody:
@@ -7727,15 +7749,19 @@ paths:
           application/json:
             schema:
               properties:
-                host_nqn:
-                  description: NVMeoF host NQN
-                  type: string
-                subsystem_nqn:
+                enable_ha:
+                  description: Enable high availability
+                  type: boolean
+                max_namespaces:
+                  default: 256
+                  description: Maximum number of namespaces
+                  type: integer
+                nqn:
                   description: NVMeoF subsystem NQN
                   type: string
               required:
-              - subsystem_nqn
-              - host_nqn
+              - nqn
+              - enable_ha
               type: object
       responses:
         '201':
@@ -7759,15 +7785,112 @@ paths:
             trace.
       security:
       - jwt: []
-      summary: Allow hosts to access an NVMeoF subsystem
+      summary: Create a new NVMeoF subsystem
+      tags:
+      - NVMe-oF Subsystem
+  /api/nvmeof/subsystem/{nqn}:
+    delete:
+      parameters:
+      - description: NVMeoF subsystem NQN
+        in: path
+        name: nqn
+        required: true
+        schema:
+          type: string
+      - default: 'false'
+        description: Force delete
+        in: query
+        name: force
+        schema:
+          type: boolean
+      responses:
+        '202':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Operation is still executing. Please check the task queue.
+        '204':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Resource deleted.
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      summary: Delete an existing NVMeoF subsystem
       tags:
-      - NVMe-oF
-  /api/nvmeof/hosts/{subsystem_nqn}:
+      - NVMe-oF Subsystem
     get:
       parameters:
       - description: NVMeoF subsystem NQN
         in: path
-        name: subsystem_nqn
+        name: nqn
+        required: true
+        schema:
+          type: string
+      responses:
+        '200':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: OK
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      summary: Get information from a specific NVMeoF subsystem
+      tags:
+      - NVMe-oF Subsystem
+  /api/nvmeof/subsystem/{nqn}/connection:
+    get:
+      parameters:
+      - description: NVMeoF subsystem NQN
+        in: path
+        name: nqn
+        required: true
+        schema:
+          type: string
+      responses:
+        '200':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: OK
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      summary: List all NVMeoF Subsystem Connections
+      tags:
+      - NVMe-oF Subsystem Connection
+  /api/nvmeof/subsystem/{nqn}/host:
+    get:
+      parameters:
+      - description: NVMeoF subsystem NQN
+        in: path
+        name: nqn
         required: true
         schema:
           type: string
@@ -7790,17 +7913,61 @@ paths:
       - jwt: []
       summary: List all allowed hosts for an NVMeoF subsystem
       tags:
-      - NVMe-oF
-  /api/nvmeof/hosts/{subsystem_nqn}/{host_nqn}:
+      - NVMe-oF Subsystem Host Allowlist
+    post:
+      parameters:
+      - description: NVMeoF subsystem NQN
+        in: path
+        name: nqn
+        required: true
+        schema:
+          type: string
+      requestBody:
+        content:
+          application/json:
+            schema:
+              properties:
+                host_nqn:
+                  description: NVMeoF host NQN. Use "*" to allow any host.
+                  type: string
+              required:
+              - host_nqn
+              type: object
+      responses:
+        '201':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Resource created.
+        '202':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: Operation is still executing. Please check the task queue.
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      summary: Allow hosts to access an NVMeoF subsystem
+      tags:
+      - NVMe-oF Subsystem Host Allowlist
+  /api/nvmeof/subsystem/{nqn}/host/{host_nqn}:
     delete:
       parameters:
       - description: NVMeoF subsystem NQN
         in: path
-        name: subsystem_nqn
+        name: nqn
         required: true
         schema:
           type: string
-      - description: NVMeoF host NQN
+      - description: NVMeoF host NQN. Use "*" to disallow any host.
         in: path
         name: host_nqn
         required: true
@@ -7830,13 +7997,13 @@ paths:
       - jwt: []
       summary: Disallow hosts from accessing an NVMeoF subsystem
       tags:
-      - NVMe-oF
-  /api/nvmeof/listener:
+      - NVMe-oF Subsystem Host Allowlist
+  /api/nvmeof/subsystem/{nqn}/listener:
     get:
       parameters:
       - description: NVMeoF subsystem NQN
-        in: query
-        name: subsystem_nqn
+        in: path
+        name: nqn
         required: true
         schema:
           type: string
@@ -7859,26 +8026,37 @@ paths:
       - jwt: []
       summary: List all NVMeoF listeners
       tags:
-      - NVMe-oF
+      - NVMe-oF Subsystem Listener
     post:
-      parameters: []
+      parameters:
+      - description: NVMeoF subsystem NQN
+        in: path
+        name: nqn
+        required: true
+        schema:
+          type: string
       requestBody:
         content:
           application/json:
             schema:
               properties:
+                adrfam:
+                  default: ipv4
+                  description: NVMeoF address family
+                  type: string
                 gateway:
                   description: NVMeoF gateway
                   type: string
-                nqn:
-                  description: NVMeoF subsystem NQN
-                  type: string
                 traddr:
                   description: NVMeoF transport address
                   type: string
+                trsvcid:
+                  default: 4420
+                  description: NVMeoF transport service port
+                  type: integer
               required:
-              - nqn
               - gateway
+              - traddr
               type: object
       responses:
         '201':
@@ -7904,8 +8082,8 @@ paths:
       - jwt: []
       summary: Create a new NVMeoF listener
       tags:
-      - NVMe-oF
-  /api/nvmeof/listener/{nqn}/{gateway}:
+      - NVMe-oF Subsystem Listener
+  /api/nvmeof/subsystem/{nqn}/listener/{gateway}:
     delete:
       parameters:
       - description: NVMeoF subsystem NQN
@@ -7926,6 +8104,11 @@ paths:
         name: traddr
         schema:
           type: string
+      - default: 4420
+        in: query
+        name: trsvcid
+        schema:
+          type: integer
       responses:
         '202':
           content:
@@ -7950,10 +8133,44 @@ paths:
       - jwt: []
       summary: Delete an existing NVMeoF listener
       tags:
-      - NVMe-oF
-  /api/nvmeof/namespace:
+      - NVMe-oF Subsystem Listener
+  /api/nvmeof/subsystem/{nqn}/namespace:
+    get:
+      parameters:
+      - description: NVMeoF subsystem NQN
+        in: path
+        name: nqn
+        required: true
+        schema:
+          type: string
+      responses:
+        '200':
+          content:
+            application/vnd.ceph.api.v1.0+json:
+              type: object
+          description: OK
+        '400':
+          description: Operation exception. Please check the response body for details.
+        '401':
+          description: Unauthenticated access. Please login first.
+        '403':
+          description: Unauthorized access. Please check your permissions.
+        '500':
+          description: Unexpected error. Please check the response body for the stack
+            trace.
+      security:
+      - jwt: []
+      summary: List all NVMeoF namespaces in a subsystem
+      tags:
+      - NVMe-oF Subsystem Namespace
     post:
-      parameters: []
+      parameters:
+      - description: NVMeoF subsystem NQN
+        in: path
+        name: nqn
+        required: true
+        schema:
+          type: string
       requestBody:
         content:
           application/json:
@@ -7967,23 +8184,22 @@ paths:
                   default: true
                   description: Create RBD image
                   type: boolean
-                image_size:
-                  default: 1024
-                  description: RBD image size
+                load_balancing_group:
+                  description: Load balancing group
                   type: integer
-                rbd_image:
+                rbd_image_name:
                   description: RBD image name
                   type: string
                 rbd_pool:
+                  default: rbd
                   description: RBD pool name
                   type: string
-                subsystem_nqn:
-                  description: NVMeoF subsystem NQN
-                  type: string
+                size:
+                  default: 1024
+                  description: RBD image size
+                  type: integer
               required:
-              - rbd_pool
-              - rbd_image
-              - subsystem_nqn
+              - rbd_image_name
               type: object
       responses:
         '201':
@@ -8009,13 +8225,19 @@ paths:
       - jwt: []
       summary: Create a new NVMeoF namespace
       tags:
-      - NVMe-oF
-  /api/nvmeof/namespace/{subsystem_nqn}:
+      - NVMe-oF Subsystem Namespace
+  /api/nvmeof/subsystem/{nqn}/namespace/{nsid}:
     delete:
       parameters:
       - description: NVMeoF subsystem NQN
         in: path
-        name: subsystem_nqn
+        name: nqn
+        required: true
+        schema:
+          type: string
+      - description: NVMeoF Namespace ID
+        in: path
+        name: nsid
         required: true
         schema:
           type: string
@@ -8043,12 +8265,18 @@ paths:
       - jwt: []
       summary: Delete an existing NVMeoF namespace
       tags:
-      - NVMe-oF
+      - NVMe-oF Subsystem Namespace
     get:
       parameters:
       - description: NVMeoF subsystem NQN
         in: path
-        name: subsystem_nqn
+        name: nqn
+        required: true
+        schema:
+          type: string
+      - description: NVMeoF Namespace ID
+        in: path
+        name: nsid
         required: true
         schema:
           type: string
@@ -8069,62 +8297,53 @@ paths:
             trace.
       security:
       - jwt: []
-      summary: List all NVMeoF namespaces
+      summary: Get info from specified NVMeoF namespace
       tags:
-      - NVMe-oF
-  /api/nvmeof/subsystem:
-    get:
+      - NVMe-oF Subsystem Namespace
+    patch:
       parameters:
-      - allowEmptyValue: true
-        description: NVMeoF subsystem NQN
-        in: query
-        name: subsystem_nqn
+      - description: NVMeoF subsystem NQN
+        in: path
+        name: nqn
+        required: true
+        schema:
+          type: string
+      - description: NVMeoF Namespace ID
+        in: path
+        name: nsid
+        required: true
         schema:
           type: string
-      responses:
-        '200':
-          content:
-            application/vnd.ceph.api.v1.0+json:
-              type: object
-          description: OK
-        '400':
-          description: Operation exception. Please check the response body for details.
-        '401':
-          description: Unauthenticated access. Please login first.
-        '403':
-          description: Unauthorized access. Please check your permissions.
-        '500':
-          description: Unexpected error. Please check the response body for the stack
-            trace.
-      security:
-      - jwt: []
-      summary: List all NVMeoF subsystems
-      tags:
-      - NVMe-oF
-    post:
-      parameters: []
       requestBody:
         content:
           application/json:
             schema:
               properties:
-                subsystem_nqn:
-                  description: NVMeoF subsystem NQN
-                  type: string
-              required:
-              - subsystem_nqn
+                load_balancing_group:
+                  description: Load balancing group
+                  type: integer
+                r_mbytes_per_second:
+                  description: Read MB/s
+                  type: integer
+                rbd_image_size:
+                  description: RBD image size
+                  type: integer
+                rw_ios_per_second:
+                  description: Read/Write IOPS
+                  type: integer
+                rw_mbytes_per_second:
+                  description: Read/Write MB/s
+                  type: integer
+                w_mbytes_per_second:
+                  description: Write MB/s
+                  type: integer
               type: object
       responses:
-        '201':
-          content:
-            application/vnd.ceph.api.v1.0+json:
-              type: object
-          description: Resource created.
-        '202':
+        '200':
           content:
             application/vnd.ceph.api.v1.0+json:
               type: object
-          description: Operation is still executing. Please check the task queue.
+          description: Resource updated.
         '400':
           description: Operation exception. Please check the response body for details.
         '401':
@@ -8136,35 +8355,30 @@ paths:
             trace.
       security:
       - jwt: []
-      summary: Create a new NVMeoF subsystem
+      summary: Update an existing NVMeoF namespace
       tags:
-      - NVMe-oF
-  /api/nvmeof/subsystem/{subsystem_nqn}:
-    delete:
+      - NVMe-oF Subsystem Namespace
+  /api/nvmeof/subsystem/{nqn}/namespace/{nsid}/io_stats:
+    get:
       parameters:
       - description: NVMeoF subsystem NQN
         in: path
-        name: subsystem_nqn
+        name: nqn
         required: true
         schema:
           type: string
-      - default: false
-        description: Force delete
-        in: query
-        name: force
+      - description: NVMeoF Namespace ID
+        in: path
+        name: nsid
+        required: true
         schema:
-          type: boolean
+          type: string
       responses:
-        '202':
-          content:
-            application/vnd.ceph.api.v1.0+json:
-              type: object
-          description: Operation is still executing. Please check the task queue.
-        '204':
+        '200':
           content:
             application/vnd.ceph.api.v1.0+json:
               type: object
-          description: Resource deleted.
+          description: OK
         '400':
           description: Operation exception. Please check the response body for details.
         '401':
@@ -8176,9 +8390,9 @@ paths:
             trace.
       security:
       - jwt: []
-      summary: Delete an existing NVMeoF subsystem
+      summary: Get IO stats from specified NVMeoF namespace
       tags:
-      - NVMe-oF
+      - NVMe-oF Subsystem Namespace
   /api/osd:
     get:
       parameters: []
@@ -14048,7 +14262,17 @@ tags:
 - description: NFS-Ganesha Cluster Management API
   name: NFS-Ganesha
 - description: NVMe-oF Gateway Management API
-  name: NVMe-oF
+  name: NVMe-oF Gateway
+- description: NVMe-oF Subsystem Management API
+  name: NVMe-oF Subsystem
+- description: NVMe-oF Subsystem Connection Management API
+  name: NVMe-oF Subsystem Connection
+- description: NVMe-oF Subsystem Host Allowlist Management API
+  name: NVMe-oF Subsystem Host Allowlist
+- description: NVMe-oF Subsystem Listener Management API
+  name: NVMe-oF Subsystem Listener
+- description: NVMe-oF Subsystem Namespace Management API
+  name: NVMe-oF Subsystem Namespace
 - description: OSD management API
   name: OSD
 - description: OSD Perf Counters Management API
index f7fc52bf92d8290e105b23de2674f291f035d80c..b5c78ac8bec405ea7704af45113db1e0acdb7343 100644 (file)
@@ -6,7 +6,7 @@ requests
 Routes
 -e ../../../python-common
 prettytable
-pytest==8.0.2
+pytest==7.0.1
 pyyaml
 natsort
 setuptools
index c39209569dbd7c9d91cac57dc5936c1bce0195f0..e8e019b0f71e6fe6aaca9b804ab508de98f1fd06 100644 (file)
@@ -49,9 +49,14 @@ def dashboard_exception_handler(handler, *args, **kwargs):
     except (cherrypy.HTTPRedirect, cherrypy.NotFound, cherrypy.HTTPError):
         raise
     except (ViewCacheNoDataException, DashboardException) as error:
-        logger.exception('Dashboard Exception')
+        http_status = getattr(error, 'status', 400)
+        cherrypy.response.status = http_status
         cherrypy.response.headers['Content-Type'] = 'application/json'
-        cherrypy.response.status = getattr(error, 'status', 400)
+
+        if http_status >= 500:
+            logger.exception('Dashboard Exception')
+        else:
+            logger.info('Dashboard Exception: %s', error)
         return json.dumps(serialize_dashboard_exception(error)).encode('utf-8')
     except Exception as error:
         logger.exception('Internal Server Error')
index c51d7e80edb70a4946393f6c49af08b25463aea8..5dee7dfcfbc3b1d3a8ce17885fe83117cc5e3717 100644 (file)
+import functools
 import logging
-from typing import Optional
+from collections.abc import Iterable
+from typing import Any, Callable, Dict, List, NamedTuple, Optional, Type
 
+from ..exceptions import DashboardException
 from .nvmeof_conf import NvmeofGatewaysConfig
 
-logger = logging.getLogger('nvmeof_client')
+logger = logging.getLogger("nvmeof_client")
 
 try:
-    import grpc
+    import grpc  # type: ignore
+    import grpc._channel  # type: ignore
+    from google.protobuf.message import Message  # type: ignore
 
     from .proto import gateway_pb2 as pb2
     from .proto import gateway_pb2_grpc as pb2_grpc
 except ImportError:
     grpc = None
 else:
+
     class NVMeoFClient(object):
+        pb2 = pb2
+
         def __init__(self):
-            logger.info('Initiating nvmeof gateway connection...')
-
-            self.gateway_addr = list(NvmeofGatewaysConfig.get_gateways_config()[
-                                     'gateways'].values())[0]['service_url']
-            self.channel = grpc.insecure_channel(
-                '{}'.format(self.gateway_addr)
-            )
-            logger.info('Found nvmeof gateway at %s', self.gateway_addr)
+            logger.info("Initiating nvmeof gateway connection...")
+
+            self.gateway_addr = list(
+                NvmeofGatewaysConfig.get_gateways_config()["gateways"].values()
+            )[0]["service_url"]
+            self.channel = grpc.insecure_channel("{}".format(self.gateway_addr))
+            logger.info("Found nvmeof gateway at %s", self.gateway_addr)
             self.stub = pb2_grpc.GatewayStub(self.channel)
 
-        def list_subsystems(self, subsystem_nqn: Optional[str] = None):
-            return self.stub.list_subsystems(pb2.list_subsystems_req(
-                subsystem_nqn=subsystem_nqn
-            ))
-
-        def create_subsystem(self, subsystem_nqn: str):
-            return self.stub.create_subsystem(pb2.create_subsystem_req(
-                subsystem_nqn=subsystem_nqn
-            ))
-
-        def delete_subsystem(self, subsystem_nqn: str, force: Optional[bool] = False):
-            return self.stub.delete_subsystem(pb2.delete_subsystem_req(
-                subsystem_nqn=subsystem_nqn,
-                force=force
-            ))
-
-        def list_namespaces(self, subsystem_nqn: str):
-            return self.stub.list_namespaces(pb2.list_namespaces_req(
-                subsystem=subsystem_nqn
-            ))
-
-        def create_namespace(self, rbd_pool_name: str, rbd_image_name: str,
-                             subsystem_nqn: str, block_size: int = 512,
-                             create_image: Optional[bool] = True,
-                             size: Optional[int] = 1024):
-            return self.stub.namespace_add(pb2.namespace_add_req(
-                rbd_pool_name=rbd_pool_name,
-                rbd_image_name=rbd_image_name,
-                subsystem_nqn=subsystem_nqn,
-                block_size=block_size,
-                create_image=create_image,
-                size=size
-            ))
-
-        def delete_namespace(self, subsystem_nqn: str):
-            return self.stub.namespace_delete(pb2.namespace_delete_req(
-                subsystem_nqn=subsystem_nqn
-            ))
-
-        def list_hosts(self, subsystem_nqn: str):
-            return self.stub.list_hosts(pb2.list_hosts_req(
-                subsystem=subsystem_nqn
-            ))
-
-        def add_host(self, subsystem_nqn: str, host_nqn: str):
-            return self.stub.add_host(pb2.add_host_req(
-                subsystem_nqn=subsystem_nqn,
-                host_nqn=host_nqn
-            ))
-
-        def remove_host(self, subsystem_nqn: str, host_nqn: str):
-            return self.stub.remove_host(pb2.remove_host_req(
-                subsystem_nqn=subsystem_nqn,
-                host_nqn=host_nqn
-            ))
-
-        def list_listeners(self, subsystem_nqn: str):
-            return self.stub.list_listeners(pb2.list_listeners_req(
-                subsystem=subsystem_nqn
-            ))
-
-        def create_listener(self, nqn: str, gateway: str, traddr: Optional[str] = None):
-            if traddr is None:
-                addr = self.gateway_addr
-                ip_address, _ = addr.split(':')
-                traddr = self._escape_address_if_ipv6(ip_address)
-
-            req = pb2.create_listener_req(
-                nqn=nqn,
-                gateway_name=gateway,
-                traddr=traddr
-            )
-            return self.stub.create_listener(req)
-
-        def delete_listener(self, nqn: str, gateway: str, traddr: Optional[str] = None):
-            if traddr is None:
-                addr = self.gateway_addr
-                ip_address, _ = addr.split(':')
-                traddr = self._escape_address_if_ipv6(ip_address)
-
-            return self.stub.delete_listener(pb2.delete_listener_req(
-                nqn=nqn,
-                gateway_name=gateway,
-                traddr=traddr
-            ))
-
-        def gateway_info(self):
-            return self.stub.get_gateway_info(pb2.get_gateway_info_req())
-
-        def _escape_address_if_ipv6(self, addr):
-            ret_addr = addr
-            if ":" in addr and not addr.strip().startswith("["):
-                ret_addr = f"[{addr}]"
-            return ret_addr
+    def make_namedtuple_from_object(cls: Type[NamedTuple], obj: Any) -> NamedTuple:
+        return cls(
+            **{
+                field: getattr(obj, field)
+                for field in cls._fields
+                if hasattr(obj, field)
+            }
+        )  # type: ignore
+
+    Model = Dict[str, Any]
+
+    def map_model(
+        model: Type[NamedTuple],
+        first: Optional[str] = None,
+    ) -> Callable[..., Callable[..., Model]]:
+        def decorator(func: Callable[..., Message]) -> Callable[..., Model]:
+            @functools.wraps(func)
+            def wrapper(*args, **kwargs) -> Model:
+                message = func(*args, **kwargs)
+                if first:
+                    try:
+                        message = getattr(message, first)[0]
+                    except IndexError:
+                        raise DashboardException(
+                            msg="Not Found", http_status_code=404, component="nvmeof"
+                        )
+
+                return make_namedtuple_from_object(model, message)._asdict()
+
+            return wrapper
+
+        return decorator
+
+    Collection = List[Model]
+
+    def map_collection(
+        model: Type[NamedTuple],
+        pick: str,
+        finalize: Optional[Callable[[Message, Collection], Collection]] = None,
+    ) -> Callable[..., Callable[..., Collection]]:
+        def decorator(func: Callable[..., Message]) -> Callable[..., Collection]:
+            @functools.wraps(func)
+            def wrapper(*args, **kwargs) -> Collection:
+                message = func(*args, **kwargs)
+                collection: Iterable = getattr(message, pick)
+                out = [
+                    make_namedtuple_from_object(model, i)._asdict() for i in collection
+                ]
+                if finalize:
+                    return finalize(message, out)
+                return out
+
+            return wrapper
+
+        return decorator
+
+    import errno
+
+    NVMeoFError2HTTP = {
+        # errno errors
+        errno.EPERM: 403,  # 1
+        errno.ENOENT: 404,  # 2
+        errno.EACCES: 403,  # 13
+        errno.EEXIST: 409,  # 17
+        errno.ENODEV: 404,  # 19
+        # JSONRPC Spec: https://www.jsonrpc.org/specification#error_object
+        -32602: 422,  # Invalid Params
+        -32603: 500,  # Internal Error
+    }
+
+    def handle_nvmeof_error(func: Callable[..., Message]) -> Callable[..., Message]:
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs) -> Message:
+            try:
+                response = func(*args, **kwargs)
+            except grpc._channel._InactiveRpcError as e:  # pylint: disable=protected-access
+                raise DashboardException(
+                    msg=e.details(),
+                    code=e.code(),
+                    http_status_code=504,
+                    component="nvmeof",
+                )
+
+            if response.status != 0:
+                raise DashboardException(
+                    msg=response.error_message,
+                    code=response.status,
+                    http_status_code=NVMeoFError2HTTP.get(response.status, 400),
+                    component="nvmeof",
+                )
+            return response
+
+        return wrapper
+
+    def empty_response(func: Callable[..., Message]) -> Callable[..., None]:
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs) -> None:
+            func(*args, **kwargs)
+
+        return wrapper
index 81e2b7205ceb1460ab96b15759fe1796b67c5614..b2698872a90ca61dc1a91978ad9b89dcca884247 100644 (file)
@@ -105,7 +105,7 @@ message namespace_add_req {
        optional string uuid = 6;
        optional int32 anagrpid = 7;
        optional bool create_image = 8;
-       optional uint32 size = 9;
+       optional uint64 size = 9;
        optional bool force = 10;
 }
 
@@ -113,7 +113,7 @@ message namespace_resize_req {
        string subsystem_nqn = 1;
        optional uint32 nsid = 2;
        optional string uuid = 3;
-       uint32 new_size = 4;
+       uint64 new_size = 4;
 }
 
 message namespace_get_io_stats_req {
@@ -319,6 +319,7 @@ message subsystem_cli {
        uint32 max_cntlid = 6;
        uint32 namespace_count = 7;
        string subtype = 8;
+       uint32 max_namespaces = 9;
 }
 
 message gateway_info {
@@ -332,6 +333,7 @@ message gateway_info {
        int32 status = 8;
        string error_message = 9;
        optional string spdk_version = 10;
+        uint32 load_balancing_group = 11;
 }
 
 message cli_version {
@@ -453,4 +455,4 @@ message spdk_nvmf_log_flags_and_level_info {
        repeated spdk_log_flag_info nvmf_log_flags = 3;
        LogLevel log_level = 4;
        LogLevel log_print_level = 5;
-}
\ No newline at end of file
+}
index 6bd40aaee4739ba96a9763fa2945d85b7a7fa502..6d807fc27a76b3825b38a9e8123c7491eb701ae3 100644 (file)
@@ -19,7 +19,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
     syntax='proto3',
     serialized_options=None,
     create_key=_descriptor._internal_create_key,
-    serialized_pb=b'\n\rgateway.proto\"\x91\x02\n\x11namespace_add_req\x12\x15\n\rrbd_pool_name\x18\x01 \x01(\t\x12\x16\n\x0erbd_image_name\x18\x02 \x01(\t\x12\x15\n\rsubsystem_nqn\x18\x03 \x01(\t\x12\x11\n\x04nsid\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x12\n\nblock_size\x18\x05 \x01(\r\x12\x11\n\x04uuid\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x15\n\x08\x61nagrpid\x18\x07 \x01(\x05H\x02\x88\x01\x01\x12\x19\n\x0c\x63reate_image\x18\x08 \x01(\x08H\x03\x88\x01\x01\x12\x11\n\x04size\x18\t \x01(\rH\x04\x88\x01\x01\x42\x07\n\x05_nsidB\x07\n\x05_uuidB\x0b\n\t_anagrpidB\x0f\n\r_create_imageB\x07\n\x05_size\"w\n\x14namespace_resize_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x10\n\x08new_size\x18\x04 \x01(\rB\x07\n\x05_nsidB\x07\n\x05_uuid\"k\n\x1anamespace_get_io_stats_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_nsidB\x07\n\x05_uuid\"\xcc\x02\n\x15namespace_set_qos_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x1e\n\x11rw_ios_per_second\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12!\n\x14rw_mbytes_per_second\x18\x05 \x01(\x04H\x03\x88\x01\x01\x12 \n\x13r_mbytes_per_second\x18\x06 \x01(\x04H\x04\x88\x01\x01\x12 \n\x13w_mbytes_per_second\x18\x07 \x01(\x04H\x05\x88\x01\x01\x42\x07\n\x05_nsidB\x07\n\x05_uuidB\x14\n\x12_rw_ios_per_secondB\x17\n\x15_rw_mbytes_per_secondB\x16\n\x14_r_mbytes_per_secondB\x16\n\x14_w_mbytes_per_second\"\x8c\x01\n)namespace_change_load_balancing_group_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x10\n\x08\x61nagrpid\x18\x04 \x01(\x05\x42\x07\n\x05_nsidB\x07\n\x05_uuid\"e\n\x14namespace_delete_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_nsidB\x07\n\x05_uuid\"\x87\x01\n\x14\x63reate_subsystem_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x15\n\rserial_number\x18\x02 \x01(\t\x12\x1b\n\x0emax_namespaces\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x11\n\tenable_ha\x18\x04 \x01(\x08\x42\x11\n\x0f_max_namespaces\"K\n\x14\x64\x65lete_subsystem_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"`\n\x13list_namespaces_req\x12\x11\n\tsubsystem\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_nsidB\x07\n\x05_uuid\"7\n\x0c\x61\x64\x64_host_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x10\n\x08host_nqn\x18\x02 \x01(\t\":\n\x0fremove_host_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x10\n\x08host_nqn\x18\x02 \x01(\t\"#\n\x0elist_hosts_req\x12\x11\n\tsubsystem\x18\x01 \x01(\t\")\n\x14list_connections_req\x12\x11\n\tsubsystem\x18\x01 \x01(\t\"\x9a\x01\n\x13\x63reate_listener_req\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12\x14\n\x0cgateway_name\x18\x02 \x01(\t\x12\x0e\n\x06traddr\x18\x03 \x01(\t\x12#\n\x06\x61\x64rfam\x18\x05 \x01(\x0e\x32\x0e.AddressFamilyH\x00\x88\x01\x01\x12\x14\n\x07trsvcid\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\t\n\x07_adrfamB\n\n\x08_trsvcid\"\x9a\x01\n\x13\x64\x65lete_listener_req\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12\x14\n\x0cgateway_name\x18\x02 \x01(\t\x12\x0e\n\x06traddr\x18\x03 \x01(\t\x12#\n\x06\x61\x64rfam\x18\x05 \x01(\x0e\x32\x0e.AddressFamilyH\x00\x88\x01\x01\x12\x14\n\x07trsvcid\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\t\n\x07_adrfamB\n\n\x08_trsvcid\"\'\n\x12list_listeners_req\x12\x11\n\tsubsystem\x18\x01 \x01(\t\"q\n\x13list_subsystems_req\x12\x1a\n\rsubsystem_nqn\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rserial_number\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x10\n\x0e_subsystem_nqnB\x10\n\x0e_serial_number\"\x14\n\x12get_subsystems_req\"\'\n%get_spdk_nvmf_log_flags_and_level_req\"\x1c\n\x1a\x64isable_spdk_nvmf_logs_req\"~\n\x16set_spdk_nvmf_logs_req\x12!\n\tlog_level\x18\x01 \x01(\x0e\x32\t.LogLevelH\x00\x88\x01\x01\x12#\n\x0bprint_level\x18\x02 \x01(\x0e\x32\t.LogLevelH\x01\x88\x01\x01\x42\x0c\n\n_log_levelB\x0e\n\x0c_print_level\"@\n\x14get_gateway_info_req\x12\x18\n\x0b\x63li_version\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_cli_version\"<\n\x0f\x61na_group_state\x12\x0e\n\x06grp_id\x18\x01 \x01(\r\x12\x19\n\x05state\x18\x02 \x01(\x0e\x32\n.ana_state\"?\n\x0enqn_ana_states\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12 \n\x06states\x18\x02 \x03(\x0b\x32\x10.ana_group_state\"+\n\x08\x61na_info\x12\x1f\n\x06states\x18\x01 \x03(\x0b\x32\x0f.nqn_ana_states\"3\n\nreq_status\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\"B\n\x0bnsid_status\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x0c\n\x04nsid\x18\x03 \x01(\r\"1\n\x0fsubsystems_info\x12\x1e\n\nsubsystems\x18\x01 \x03(\x0b\x32\n.subsystem\"\xfc\x02\n\tsubsystem\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12\x0f\n\x07subtype\x18\x02 \x01(\t\x12)\n\x10listen_addresses\x18\x03 \x03(\x0b\x32\x0f.listen_address\x12\x14\n\x05hosts\x18\x04 \x03(\x0b\x32\x05.host\x12\x16\n\x0e\x61llow_any_host\x18\x05 \x01(\x08\x12\x1a\n\rserial_number\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cmodel_number\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0emax_namespaces\x18\x08 \x01(\rH\x02\x88\x01\x01\x12\x17\n\nmin_cntlid\x18\t \x01(\rH\x03\x88\x01\x01\x12\x17\n\nmax_cntlid\x18\n \x01(\rH\x04\x88\x01\x01\x12\x1e\n\nnamespaces\x18\x0b \x03(\x0b\x32\n.namespaceB\x10\n\x0e_serial_numberB\x0f\n\r_model_numberB\x11\n\x0f_max_namespacesB\r\n\x0b_min_cntlidB\r\n\x0b_max_cntlid\"w\n\x0elisten_address\x12\x0e\n\x06trtype\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x64rfam\x18\x02 \x01(\t\x12\x0e\n\x06traddr\x18\x03 \x01(\t\x12\x0f\n\x07trsvcid\x18\x04 \x01(\t\x12\x16\n\ttransport\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_transport\"\xc9\x01\n\tnamespace\x12\x0c\n\x04nsid\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\tbdev_name\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05nguid\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04uuid\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x15\n\x08\x61nagrpid\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x12\n\x05nonce\x18\x07 \x01(\tH\x04\x88\x01\x01\x42\x0c\n\n_bdev_nameB\x08\n\x06_nguidB\x07\n\x05_uuidB\x0b\n\t_anagrpidB\x08\n\x06_nonce\"`\n\x13subsystems_info_cli\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\"\n\nsubsystems\x18\x03 \x03(\x0b\x32\x0e.subsystem_cli\"\xae\x01\n\rsubsystem_cli\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12\x11\n\tenable_ha\x18\x02 \x01(\x08\x12\x15\n\rserial_number\x18\x03 \x01(\t\x12\x14\n\x0cmodel_number\x18\x04 \x01(\t\x12\x12\n\nmin_cntlid\x18\x05 \x01(\r\x12\x12\n\nmax_cntlid\x18\x06 \x01(\r\x12\x17\n\x0fnamespace_count\x18\x07 \x01(\r\x12\x0f\n\x07subtype\x18\x08 \x01(\t\"\xd5\x01\n\x0cgateway_info\x12\x13\n\x0b\x63li_version\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\r\n\x05group\x18\x04 \x01(\t\x12\x0c\n\x04\x61\x64\x64r\x18\x05 \x01(\t\x12\x0c\n\x04port\x18\x06 \x01(\t\x12\x13\n\x0b\x62ool_status\x18\x07 \x01(\x08\x12\x0e\n\x06status\x18\x08 \x01(\x05\x12\x15\n\rerror_message\x18\t \x01(\t\x12\x19\n\x0cspdk_version\x18\n \x01(\tH\x00\x88\x01\x01\x42\x0f\n\r_spdk_version\"E\n\x0b\x63li_version\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\"D\n\ngw_version\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\"v\n\rlistener_info\x12\x14\n\x0cgateway_name\x18\x01 \x01(\t\x12\x0e\n\x06trtype\x18\x02 \x01(\t\x12\x1e\n\x06\x61\x64rfam\x18\x03 \x01(\x0e\x32\x0e.AddressFamily\x12\x0e\n\x06traddr\x18\x04 \x01(\t\x12\x0f\n\x07trsvcid\x18\x05 \x01(\r\"Z\n\x0elisteners_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12!\n\tlisteners\x18\x03 \x03(\x0b\x32\x0e.listener_info\"\x13\n\x04host\x12\x0b\n\x03nqn\x18\x01 \x01(\t\"x\n\nhosts_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x16\n\x0e\x61llow_any_host\x18\x03 \x01(\x08\x12\x15\n\rsubsystem_nqn\x18\x04 \x01(\t\x12\x14\n\x05hosts\x18\x05 \x03(\x0b\x32\x05.host\"\xaa\x01\n\nconnection\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12\x0e\n\x06traddr\x18\x02 \x01(\t\x12\x0f\n\x07trsvcid\x18\x03 \x01(\r\x12\x0e\n\x06trtype\x18\x04 \x01(\t\x12\x1e\n\x06\x61\x64rfam\x18\x05 \x01(\x0e\x32\x0e.AddressFamily\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12\x14\n\x0cqpairs_count\x18\x07 \x01(\x05\x12\x15\n\rcontroller_id\x18\x08 \x01(\x05\"r\n\x10\x63onnections_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x15\n\rsubsystem_nqn\x18\x03 \x01(\t\x12 \n\x0b\x63onnections\x18\x04 \x03(\x0b\x32\x0b.connection\"\xaa\x02\n\rnamespace_cli\x12\x0c\n\x04nsid\x18\x01 \x01(\r\x12\x11\n\tbdev_name\x18\x02 \x01(\t\x12\x16\n\x0erbd_image_name\x18\x03 \x01(\t\x12\x15\n\rrbd_pool_name\x18\x04 \x01(\t\x12\x1c\n\x14load_balancing_group\x18\x05 \x01(\r\x12\x12\n\nblock_size\x18\x06 \x01(\r\x12\x16\n\x0erbd_image_size\x18\x07 \x01(\x04\x12\x0c\n\x04uuid\x18\x08 \x01(\t\x12\x19\n\x11rw_ios_per_second\x18\t \x01(\x04\x12\x1c\n\x14rw_mbytes_per_second\x18\n \x01(\x04\x12\x1b\n\x13r_mbytes_per_second\x18\x0b \x01(\x04\x12\x1b\n\x13w_mbytes_per_second\x18\x0c \x01(\x04\"s\n\x0fnamespaces_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x15\n\rsubsystem_nqn\x18\x03 \x01(\t\x12\"\n\nnamespaces\x18\x04 \x03(\x0b\x32\x0e.namespace_cli\"\xb7\x05\n\x17namespace_io_stats_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x15\n\rsubsystem_nqn\x18\x03 \x01(\t\x12\x0c\n\x04nsid\x18\x04 \x01(\r\x12\x0c\n\x04uuid\x18\x05 \x01(\t\x12\x11\n\tbdev_name\x18\x06 \x01(\t\x12\x11\n\ttick_rate\x18\x07 \x01(\x04\x12\r\n\x05ticks\x18\x08 \x01(\x04\x12\x12\n\nbytes_read\x18\t \x01(\x04\x12\x14\n\x0cnum_read_ops\x18\n \x01(\x04\x12\x15\n\rbytes_written\x18\x0b \x01(\x04\x12\x15\n\rnum_write_ops\x18\x0c \x01(\x04\x12\x16\n\x0e\x62ytes_unmapped\x18\r \x01(\x04\x12\x15\n\rnum_unmap_ops\x18\x0e \x01(\x04\x12\x1a\n\x12read_latency_ticks\x18\x0f \x01(\x04\x12\x1e\n\x16max_read_latency_ticks\x18\x10 \x01(\x04\x12\x1e\n\x16min_read_latency_ticks\x18\x11 \x01(\x04\x12\x1b\n\x13write_latency_ticks\x18\x12 \x01(\x04\x12\x1f\n\x17max_write_latency_ticks\x18\x13 \x01(\x04\x12\x1f\n\x17min_write_latency_ticks\x18\x14 \x01(\x04\x12\x1b\n\x13unmap_latency_ticks\x18\x15 \x01(\x04\x12\x1f\n\x17max_unmap_latency_ticks\x18\x16 \x01(\x04\x12\x1f\n\x17min_unmap_latency_ticks\x18\x17 \x01(\x04\x12\x1a\n\x12\x63opy_latency_ticks\x18\x18 \x01(\x04\x12\x1e\n\x16max_copy_latency_ticks\x18\x19 \x01(\x04\x12\x1e\n\x16min_copy_latency_ticks\x18\x1a \x01(\x04\x12\x10\n\x08io_error\x18\x1b \x03(\r\"3\n\x12spdk_log_flag_info\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\"\xba\x01\n\"spdk_nvmf_log_flags_and_level_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12+\n\x0envmf_log_flags\x18\x03 \x03(\x0b\x32\x13.spdk_log_flag_info\x12\x1c\n\tlog_level\x18\x04 \x01(\x0e\x32\t.LogLevel\x12\"\n\x0flog_print_level\x18\x05 \x01(\x0e\x32\t.LogLevel*#\n\rAddressFamily\x12\x08\n\x04ipv4\x10\x00\x12\x08\n\x04ipv6\x10\x01*C\n\x08LogLevel\x12\t\n\x05\x45RROR\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\n\n\x06NOTICE\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04*J\n\tana_state\x12\t\n\x05UNSET\x10\x00\x12\r\n\tOPTIMIZED\x10\x01\x12\x11\n\rNON_OPTIMIZED\x10\x02\x12\x10\n\x0cINACCESSIBLE\x10\x03\x32\xaa\x0b\n\x07Gateway\x12\x33\n\rnamespace_add\x12\x12.namespace_add_req\x1a\x0c.nsid_status\"\x00\x12\x38\n\x10\x63reate_subsystem\x12\x15.create_subsystem_req\x1a\x0b.req_status\"\x00\x12\x38\n\x10\x64\x65lete_subsystem\x12\x15.delete_subsystem_req\x1a\x0b.req_status\"\x00\x12;\n\x0flist_namespaces\x12\x14.list_namespaces_req\x1a\x10.namespaces_info\"\x00\x12\x38\n\x10namespace_resize\x12\x15.namespace_resize_req\x1a\x0b.req_status\"\x00\x12Q\n\x16namespace_get_io_stats\x12\x1b.namespace_get_io_stats_req\x1a\x18.namespace_io_stats_info\"\x00\x12\x41\n\x18namespace_set_qos_limits\x12\x16.namespace_set_qos_req\x1a\x0b.req_status\"\x00\x12\x62\n%namespace_change_load_balancing_group\x12*.namespace_change_load_balancing_group_req\x1a\x0b.req_status\"\x00\x12\x38\n\x10namespace_delete\x12\x15.namespace_delete_req\x1a\x0b.req_status\"\x00\x12(\n\x08\x61\x64\x64_host\x12\r.add_host_req\x1a\x0b.req_status\"\x00\x12.\n\x0bremove_host\x12\x10.remove_host_req\x1a\x0b.req_status\"\x00\x12,\n\nlist_hosts\x12\x0f.list_hosts_req\x1a\x0b.hosts_info\"\x00\x12>\n\x10list_connections\x12\x15.list_connections_req\x1a\x11.connections_info\"\x00\x12\x36\n\x0f\x63reate_listener\x12\x14.create_listener_req\x1a\x0b.req_status\"\x00\x12\x36\n\x0f\x64\x65lete_listener\x12\x14.delete_listener_req\x1a\x0b.req_status\"\x00\x12\x38\n\x0elist_listeners\x12\x13.list_listeners_req\x1a\x0f.listeners_info\"\x00\x12?\n\x0flist_subsystems\x12\x14.list_subsystems_req\x1a\x14.subsystems_info_cli\"\x00\x12\x39\n\x0eget_subsystems\x12\x13.get_subsystems_req\x1a\x10.subsystems_info\"\x00\x12)\n\rset_ana_state\x12\t.ana_info\x1a\x0b.req_status\"\x00\x12r\n!get_spdk_nvmf_log_flags_and_level\x12&.get_spdk_nvmf_log_flags_and_level_req\x1a#.spdk_nvmf_log_flags_and_level_info\"\x00\x12\x44\n\x16\x64isable_spdk_nvmf_logs\x12\x1b.disable_spdk_nvmf_logs_req\x1a\x0b.req_status\"\x00\x12<\n\x12set_spdk_nvmf_logs\x12\x17.set_spdk_nvmf_logs_req\x1a\x0b.req_status\"\x00\x12:\n\x10get_gateway_info\x12\x15.get_gateway_info_req\x1a\r.gateway_info\"\x00\x62\x06proto3'
+    serialized_pb=b'\n\rgateway.proto\"\xaf\x02\n\x11namespace_add_req\x12\x15\n\rrbd_pool_name\x18\x01 \x01(\t\x12\x16\n\x0erbd_image_name\x18\x02 \x01(\t\x12\x15\n\rsubsystem_nqn\x18\x03 \x01(\t\x12\x11\n\x04nsid\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x12\n\nblock_size\x18\x05 \x01(\r\x12\x11\n\x04uuid\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x15\n\x08\x61nagrpid\x18\x07 \x01(\x05H\x02\x88\x01\x01\x12\x19\n\x0c\x63reate_image\x18\x08 \x01(\x08H\x03\x88\x01\x01\x12\x11\n\x04size\x18\t \x01(\x04H\x04\x88\x01\x01\x12\x12\n\x05\x66orce\x18\n \x01(\x08H\x05\x88\x01\x01\x42\x07\n\x05_nsidB\x07\n\x05_uuidB\x0b\n\t_anagrpidB\x0f\n\r_create_imageB\x07\n\x05_sizeB\x08\n\x06_force\"w\n\x14namespace_resize_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x10\n\x08new_size\x18\x04 \x01(\x04\x42\x07\n\x05_nsidB\x07\n\x05_uuid\"k\n\x1anamespace_get_io_stats_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_nsidB\x07\n\x05_uuid\"\xcc\x02\n\x15namespace_set_qos_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x1e\n\x11rw_ios_per_second\x18\x04 \x01(\x04H\x02\x88\x01\x01\x12!\n\x14rw_mbytes_per_second\x18\x05 \x01(\x04H\x03\x88\x01\x01\x12 \n\x13r_mbytes_per_second\x18\x06 \x01(\x04H\x04\x88\x01\x01\x12 \n\x13w_mbytes_per_second\x18\x07 \x01(\x04H\x05\x88\x01\x01\x42\x07\n\x05_nsidB\x07\n\x05_uuidB\x14\n\x12_rw_ios_per_secondB\x17\n\x15_rw_mbytes_per_secondB\x16\n\x14_r_mbytes_per_secondB\x16\n\x14_w_mbytes_per_second\"\x8c\x01\n)namespace_change_load_balancing_group_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x10\n\x08\x61nagrpid\x18\x04 \x01(\x05\x42\x07\n\x05_nsidB\x07\n\x05_uuid\"e\n\x14namespace_delete_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_nsidB\x07\n\x05_uuid\"\x87\x01\n\x14\x63reate_subsystem_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x15\n\rserial_number\x18\x02 \x01(\t\x12\x1b\n\x0emax_namespaces\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x11\n\tenable_ha\x18\x04 \x01(\x08\x42\x11\n\x0f_max_namespaces\"K\n\x14\x64\x65lete_subsystem_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"`\n\x13list_namespaces_req\x12\x11\n\tsubsystem\x18\x01 \x01(\t\x12\x11\n\x04nsid\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04uuid\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_nsidB\x07\n\x05_uuid\"7\n\x0c\x61\x64\x64_host_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x10\n\x08host_nqn\x18\x02 \x01(\t\":\n\x0fremove_host_req\x12\x15\n\rsubsystem_nqn\x18\x01 \x01(\t\x12\x10\n\x08host_nqn\x18\x02 \x01(\t\"#\n\x0elist_hosts_req\x12\x11\n\tsubsystem\x18\x01 \x01(\t\")\n\x14list_connections_req\x12\x11\n\tsubsystem\x18\x01 \x01(\t\"\x9a\x01\n\x13\x63reate_listener_req\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12\x14\n\x0cgateway_name\x18\x02 \x01(\t\x12\x0e\n\x06traddr\x18\x03 \x01(\t\x12#\n\x06\x61\x64rfam\x18\x05 \x01(\x0e\x32\x0e.AddressFamilyH\x00\x88\x01\x01\x12\x14\n\x07trsvcid\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\t\n\x07_adrfamB\n\n\x08_trsvcid\"\x9a\x01\n\x13\x64\x65lete_listener_req\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12\x14\n\x0cgateway_name\x18\x02 \x01(\t\x12\x0e\n\x06traddr\x18\x03 \x01(\t\x12#\n\x06\x61\x64rfam\x18\x05 \x01(\x0e\x32\x0e.AddressFamilyH\x00\x88\x01\x01\x12\x14\n\x07trsvcid\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\t\n\x07_adrfamB\n\n\x08_trsvcid\"\'\n\x12list_listeners_req\x12\x11\n\tsubsystem\x18\x01 \x01(\t\"q\n\x13list_subsystems_req\x12\x1a\n\rsubsystem_nqn\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rserial_number\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x10\n\x0e_subsystem_nqnB\x10\n\x0e_serial_number\"\x14\n\x12get_subsystems_req\"\'\n%get_spdk_nvmf_log_flags_and_level_req\"\x1c\n\x1a\x64isable_spdk_nvmf_logs_req\"~\n\x16set_spdk_nvmf_logs_req\x12!\n\tlog_level\x18\x01 \x01(\x0e\x32\t.LogLevelH\x00\x88\x01\x01\x12#\n\x0bprint_level\x18\x02 \x01(\x0e\x32\t.LogLevelH\x01\x88\x01\x01\x42\x0c\n\n_log_levelB\x0e\n\x0c_print_level\"@\n\x14get_gateway_info_req\x12\x18\n\x0b\x63li_version\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x0e\n\x0c_cli_version\"<\n\x0f\x61na_group_state\x12\x0e\n\x06grp_id\x18\x01 \x01(\r\x12\x19\n\x05state\x18\x02 \x01(\x0e\x32\n.ana_state\"?\n\x0enqn_ana_states\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12 \n\x06states\x18\x02 \x03(\x0b\x32\x10.ana_group_state\"+\n\x08\x61na_info\x12\x1f\n\x06states\x18\x01 \x03(\x0b\x32\x0f.nqn_ana_states\"3\n\nreq_status\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\"B\n\x0bnsid_status\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x0c\n\x04nsid\x18\x03 \x01(\r\"1\n\x0fsubsystems_info\x12\x1e\n\nsubsystems\x18\x01 \x03(\x0b\x32\n.subsystem\"\xfc\x02\n\tsubsystem\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12\x0f\n\x07subtype\x18\x02 \x01(\t\x12)\n\x10listen_addresses\x18\x03 \x03(\x0b\x32\x0f.listen_address\x12\x14\n\x05hosts\x18\x04 \x03(\x0b\x32\x05.host\x12\x16\n\x0e\x61llow_any_host\x18\x05 \x01(\x08\x12\x1a\n\rserial_number\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cmodel_number\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x1b\n\x0emax_namespaces\x18\x08 \x01(\rH\x02\x88\x01\x01\x12\x17\n\nmin_cntlid\x18\t \x01(\rH\x03\x88\x01\x01\x12\x17\n\nmax_cntlid\x18\n \x01(\rH\x04\x88\x01\x01\x12\x1e\n\nnamespaces\x18\x0b \x03(\x0b\x32\n.namespaceB\x10\n\x0e_serial_numberB\x0f\n\r_model_numberB\x11\n\x0f_max_namespacesB\r\n\x0b_min_cntlidB\r\n\x0b_max_cntlid\"w\n\x0elisten_address\x12\x0e\n\x06trtype\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x64rfam\x18\x02 \x01(\t\x12\x0e\n\x06traddr\x18\x03 \x01(\t\x12\x0f\n\x07trsvcid\x18\x04 \x01(\t\x12\x16\n\ttransport\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x0c\n\n_transport\"\xc9\x01\n\tnamespace\x12\x0c\n\x04nsid\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x16\n\tbdev_name\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x12\n\x05nguid\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04uuid\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x15\n\x08\x61nagrpid\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x12\n\x05nonce\x18\x07 \x01(\tH\x04\x88\x01\x01\x42\x0c\n\n_bdev_nameB\x08\n\x06_nguidB\x07\n\x05_uuidB\x0b\n\t_anagrpidB\x08\n\x06_nonce\"`\n\x13subsystems_info_cli\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\"\n\nsubsystems\x18\x03 \x03(\x0b\x32\x0e.subsystem_cli\"\xc6\x01\n\rsubsystem_cli\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12\x11\n\tenable_ha\x18\x02 \x01(\x08\x12\x15\n\rserial_number\x18\x03 \x01(\t\x12\x14\n\x0cmodel_number\x18\x04 \x01(\t\x12\x12\n\nmin_cntlid\x18\x05 \x01(\r\x12\x12\n\nmax_cntlid\x18\x06 \x01(\r\x12\x17\n\x0fnamespace_count\x18\x07 \x01(\r\x12\x0f\n\x07subtype\x18\x08 \x01(\t\x12\x16\n\x0emax_namespaces\x18\t \x01(\r\"\xf3\x01\n\x0cgateway_info\x12\x13\n\x0b\x63li_version\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\r\n\x05group\x18\x04 \x01(\t\x12\x0c\n\x04\x61\x64\x64r\x18\x05 \x01(\t\x12\x0c\n\x04port\x18\x06 \x01(\t\x12\x13\n\x0b\x62ool_status\x18\x07 \x01(\x08\x12\x0e\n\x06status\x18\x08 \x01(\x05\x12\x15\n\rerror_message\x18\t \x01(\t\x12\x19\n\x0cspdk_version\x18\n \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x14load_balancing_group\x18\x0b \x01(\rB\x0f\n\r_spdk_version\"E\n\x0b\x63li_version\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\"D\n\ngw_version\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\"v\n\rlistener_info\x12\x14\n\x0cgateway_name\x18\x01 \x01(\t\x12\x0e\n\x06trtype\x18\x02 \x01(\t\x12\x1e\n\x06\x61\x64rfam\x18\x03 \x01(\x0e\x32\x0e.AddressFamily\x12\x0e\n\x06traddr\x18\x04 \x01(\t\x12\x0f\n\x07trsvcid\x18\x05 \x01(\r\"Z\n\x0elisteners_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12!\n\tlisteners\x18\x03 \x03(\x0b\x32\x0e.listener_info\"\x13\n\x04host\x12\x0b\n\x03nqn\x18\x01 \x01(\t\"x\n\nhosts_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x16\n\x0e\x61llow_any_host\x18\x03 \x01(\x08\x12\x15\n\rsubsystem_nqn\x18\x04 \x01(\t\x12\x14\n\x05hosts\x18\x05 \x03(\x0b\x32\x05.host\"\xaa\x01\n\nconnection\x12\x0b\n\x03nqn\x18\x01 \x01(\t\x12\x0e\n\x06traddr\x18\x02 \x01(\t\x12\x0f\n\x07trsvcid\x18\x03 \x01(\r\x12\x0e\n\x06trtype\x18\x04 \x01(\t\x12\x1e\n\x06\x61\x64rfam\x18\x05 \x01(\x0e\x32\x0e.AddressFamily\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12\x14\n\x0cqpairs_count\x18\x07 \x01(\x05\x12\x15\n\rcontroller_id\x18\x08 \x01(\x05\"r\n\x10\x63onnections_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x15\n\rsubsystem_nqn\x18\x03 \x01(\t\x12 \n\x0b\x63onnections\x18\x04 \x03(\x0b\x32\x0b.connection\"\xaa\x02\n\rnamespace_cli\x12\x0c\n\x04nsid\x18\x01 \x01(\r\x12\x11\n\tbdev_name\x18\x02 \x01(\t\x12\x16\n\x0erbd_image_name\x18\x03 \x01(\t\x12\x15\n\rrbd_pool_name\x18\x04 \x01(\t\x12\x1c\n\x14load_balancing_group\x18\x05 \x01(\r\x12\x12\n\nblock_size\x18\x06 \x01(\r\x12\x16\n\x0erbd_image_size\x18\x07 \x01(\x04\x12\x0c\n\x04uuid\x18\x08 \x01(\t\x12\x19\n\x11rw_ios_per_second\x18\t \x01(\x04\x12\x1c\n\x14rw_mbytes_per_second\x18\n \x01(\x04\x12\x1b\n\x13r_mbytes_per_second\x18\x0b \x01(\x04\x12\x1b\n\x13w_mbytes_per_second\x18\x0c \x01(\x04\"s\n\x0fnamespaces_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x15\n\rsubsystem_nqn\x18\x03 \x01(\t\x12\"\n\nnamespaces\x18\x04 \x03(\x0b\x32\x0e.namespace_cli\"\xb7\x05\n\x17namespace_io_stats_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x15\n\rsubsystem_nqn\x18\x03 \x01(\t\x12\x0c\n\x04nsid\x18\x04 \x01(\r\x12\x0c\n\x04uuid\x18\x05 \x01(\t\x12\x11\n\tbdev_name\x18\x06 \x01(\t\x12\x11\n\ttick_rate\x18\x07 \x01(\x04\x12\r\n\x05ticks\x18\x08 \x01(\x04\x12\x12\n\nbytes_read\x18\t \x01(\x04\x12\x14\n\x0cnum_read_ops\x18\n \x01(\x04\x12\x15\n\rbytes_written\x18\x0b \x01(\x04\x12\x15\n\rnum_write_ops\x18\x0c \x01(\x04\x12\x16\n\x0e\x62ytes_unmapped\x18\r \x01(\x04\x12\x15\n\rnum_unmap_ops\x18\x0e \x01(\x04\x12\x1a\n\x12read_latency_ticks\x18\x0f \x01(\x04\x12\x1e\n\x16max_read_latency_ticks\x18\x10 \x01(\x04\x12\x1e\n\x16min_read_latency_ticks\x18\x11 \x01(\x04\x12\x1b\n\x13write_latency_ticks\x18\x12 \x01(\x04\x12\x1f\n\x17max_write_latency_ticks\x18\x13 \x01(\x04\x12\x1f\n\x17min_write_latency_ticks\x18\x14 \x01(\x04\x12\x1b\n\x13unmap_latency_ticks\x18\x15 \x01(\x04\x12\x1f\n\x17max_unmap_latency_ticks\x18\x16 \x01(\x04\x12\x1f\n\x17min_unmap_latency_ticks\x18\x17 \x01(\x04\x12\x1a\n\x12\x63opy_latency_ticks\x18\x18 \x01(\x04\x12\x1e\n\x16max_copy_latency_ticks\x18\x19 \x01(\x04\x12\x1e\n\x16min_copy_latency_ticks\x18\x1a \x01(\x04\x12\x10\n\x08io_error\x18\x1b \x03(\r\"3\n\x12spdk_log_flag_info\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\"\xba\x01\n\"spdk_nvmf_log_flags_and_level_info\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12+\n\x0envmf_log_flags\x18\x03 \x03(\x0b\x32\x13.spdk_log_flag_info\x12\x1c\n\tlog_level\x18\x04 \x01(\x0e\x32\t.LogLevel\x12\"\n\x0flog_print_level\x18\x05 \x01(\x0e\x32\t.LogLevel*#\n\rAddressFamily\x12\x08\n\x04ipv4\x10\x00\x12\x08\n\x04ipv6\x10\x01*C\n\x08LogLevel\x12\t\n\x05\x45RROR\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\n\n\x06NOTICE\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04*J\n\tana_state\x12\t\n\x05UNSET\x10\x00\x12\r\n\tOPTIMIZED\x10\x01\x12\x11\n\rNON_OPTIMIZED\x10\x02\x12\x10\n\x0cINACCESSIBLE\x10\x03\x32\xaa\x0b\n\x07Gateway\x12\x33\n\rnamespace_add\x12\x12.namespace_add_req\x1a\x0c.nsid_status\"\x00\x12\x38\n\x10\x63reate_subsystem\x12\x15.create_subsystem_req\x1a\x0b.req_status\"\x00\x12\x38\n\x10\x64\x65lete_subsystem\x12\x15.delete_subsystem_req\x1a\x0b.req_status\"\x00\x12;\n\x0flist_namespaces\x12\x14.list_namespaces_req\x1a\x10.namespaces_info\"\x00\x12\x38\n\x10namespace_resize\x12\x15.namespace_resize_req\x1a\x0b.req_status\"\x00\x12Q\n\x16namespace_get_io_stats\x12\x1b.namespace_get_io_stats_req\x1a\x18.namespace_io_stats_info\"\x00\x12\x41\n\x18namespace_set_qos_limits\x12\x16.namespace_set_qos_req\x1a\x0b.req_status\"\x00\x12\x62\n%namespace_change_load_balancing_group\x12*.namespace_change_load_balancing_group_req\x1a\x0b.req_status\"\x00\x12\x38\n\x10namespace_delete\x12\x15.namespace_delete_req\x1a\x0b.req_status\"\x00\x12(\n\x08\x61\x64\x64_host\x12\r.add_host_req\x1a\x0b.req_status\"\x00\x12.\n\x0bremove_host\x12\x10.remove_host_req\x1a\x0b.req_status\"\x00\x12,\n\nlist_hosts\x12\x0f.list_hosts_req\x1a\x0b.hosts_info\"\x00\x12>\n\x10list_connections\x12\x15.list_connections_req\x1a\x11.connections_info\"\x00\x12\x36\n\x0f\x63reate_listener\x12\x14.create_listener_req\x1a\x0b.req_status\"\x00\x12\x36\n\x0f\x64\x65lete_listener\x12\x14.delete_listener_req\x1a\x0b.req_status\"\x00\x12\x38\n\x0elist_listeners\x12\x13.list_listeners_req\x1a\x0f.listeners_info\"\x00\x12?\n\x0flist_subsystems\x12\x14.list_subsystems_req\x1a\x14.subsystems_info_cli\"\x00\x12\x39\n\x0eget_subsystems\x12\x13.get_subsystems_req\x1a\x10.subsystems_info\"\x00\x12)\n\rset_ana_state\x12\t.ana_info\x1a\x0b.req_status\"\x00\x12r\n!get_spdk_nvmf_log_flags_and_level\x12&.get_spdk_nvmf_log_flags_and_level_req\x1a#.spdk_nvmf_log_flags_and_level_info\"\x00\x12\x44\n\x16\x64isable_spdk_nvmf_logs\x12\x1b.disable_spdk_nvmf_logs_req\x1a\x0b.req_status\"\x00\x12<\n\x12set_spdk_nvmf_logs\x12\x17.set_spdk_nvmf_logs_req\x1a\x0b.req_status\"\x00\x12:\n\x10get_gateway_info\x12\x15.get_gateway_info_req\x1a\r.gateway_info\"\x00\x62\x06proto3'
 )
 
 _ADDRESSFAMILY = _descriptor.EnumDescriptor(
@@ -42,8 +42,8 @@ _ADDRESSFAMILY = _descriptor.EnumDescriptor(
     ],
     containing_type=None,
     serialized_options=None,
-    serialized_start=6057,
-    serialized_end=6092,
+    serialized_start=6141,
+    serialized_end=6176,
 )
 _sym_db.RegisterEnumDescriptor(_ADDRESSFAMILY)
 
@@ -83,8 +83,8 @@ _LOGLEVEL = _descriptor.EnumDescriptor(
     ],
     containing_type=None,
     serialized_options=None,
-    serialized_start=6094,
-    serialized_end=6161,
+    serialized_start=6178,
+    serialized_end=6245,
 )
 _sym_db.RegisterEnumDescriptor(_LOGLEVEL)
 
@@ -119,8 +119,8 @@ _ANA_STATE = _descriptor.EnumDescriptor(
     ],
     containing_type=None,
     serialized_options=None,
-    serialized_start=6163,
-    serialized_end=6237,
+    serialized_start=6247,
+    serialized_end=6321,
 )
 _sym_db.RegisterEnumDescriptor(_ANA_STATE)
 
@@ -204,11 +204,18 @@ _NAMESPACE_ADD_REQ = _descriptor.Descriptor(
             serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
         _descriptor.FieldDescriptor(
             name='size', full_name='namespace_add_req.size', index=8,
-            number=9, type=13, cpp_type=3, label=1,
+            number=9, type=4, cpp_type=4, label=1,
             has_default_value=False, default_value=0,
             message_type=None, enum_type=None, containing_type=None,
             is_extension=False, extension_scope=None,
             serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+        _descriptor.FieldDescriptor(
+            name='force', full_name='namespace_add_req.force', index=9,
+            number=10, type=8, cpp_type=7, label=1,
+            has_default_value=False, default_value=False,
+            message_type=None, enum_type=None, containing_type=None,
+            is_extension=False, extension_scope=None,
+            serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     ],
     extensions=[
     ],
@@ -245,9 +252,14 @@ _NAMESPACE_ADD_REQ = _descriptor.Descriptor(
             index=4, containing_type=None,
             create_key=_descriptor._internal_create_key,
             fields=[]),
+        _descriptor.OneofDescriptor(
+            name='_force', full_name='namespace_add_req._force',
+            index=5, containing_type=None,
+            create_key=_descriptor._internal_create_key,
+            fields=[]),
     ],
     serialized_start=18,
-    serialized_end=291,
+    serialized_end=321,
 )
 
 
@@ -282,7 +294,7 @@ _NAMESPACE_RESIZE_REQ = _descriptor.Descriptor(
             serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
         _descriptor.FieldDescriptor(
             name='new_size', full_name='namespace_resize_req.new_size', index=3,
-            number=4, type=13, cpp_type=3, label=1,
+            number=4, type=4, cpp_type=4, label=1,
             has_default_value=False, default_value=0,
             message_type=None, enum_type=None, containing_type=None,
             is_extension=False, extension_scope=None,
@@ -309,8 +321,8 @@ _NAMESPACE_RESIZE_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=293,
-    serialized_end=412,
+    serialized_start=323,
+    serialized_end=442,
 )
 
 
@@ -365,8 +377,8 @@ _NAMESPACE_GET_IO_STATS_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=414,
-    serialized_end=521,
+    serialized_start=444,
+    serialized_end=551,
 )
 
 
@@ -469,8 +481,8 @@ _NAMESPACE_SET_QOS_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=524,
-    serialized_end=856,
+    serialized_start=554,
+    serialized_end=886,
 )
 
 
@@ -532,8 +544,8 @@ _NAMESPACE_CHANGE_LOAD_BALANCING_GROUP_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=859,
-    serialized_end=999,
+    serialized_start=889,
+    serialized_end=1029,
 )
 
 
@@ -588,8 +600,8 @@ _NAMESPACE_DELETE_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=1001,
-    serialized_end=1102,
+    serialized_start=1031,
+    serialized_end=1132,
 )
 
 
@@ -646,8 +658,8 @@ _CREATE_SUBSYSTEM_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=1105,
-    serialized_end=1240,
+    serialized_start=1135,
+    serialized_end=1270,
 )
 
 
@@ -690,8 +702,8 @@ _DELETE_SUBSYSTEM_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=1242,
-    serialized_end=1317,
+    serialized_start=1272,
+    serialized_end=1347,
 )
 
 
@@ -746,8 +758,8 @@ _LIST_NAMESPACES_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=1319,
-    serialized_end=1415,
+    serialized_start=1349,
+    serialized_end=1445,
 )
 
 
@@ -785,8 +797,8 @@ _ADD_HOST_REQ = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=1417,
-    serialized_end=1472,
+    serialized_start=1447,
+    serialized_end=1502,
 )
 
 
@@ -824,8 +836,8 @@ _REMOVE_HOST_REQ = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=1474,
-    serialized_end=1532,
+    serialized_start=1504,
+    serialized_end=1562,
 )
 
 
@@ -856,8 +868,8 @@ _LIST_HOSTS_REQ = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=1534,
-    serialized_end=1569,
+    serialized_start=1564,
+    serialized_end=1599,
 )
 
 
@@ -888,8 +900,8 @@ _LIST_CONNECTIONS_REQ = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=1571,
-    serialized_end=1612,
+    serialized_start=1601,
+    serialized_end=1642,
 )
 
 
@@ -958,8 +970,8 @@ _CREATE_LISTENER_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=1615,
-    serialized_end=1769,
+    serialized_start=1645,
+    serialized_end=1799,
 )
 
 
@@ -1028,8 +1040,8 @@ _DELETE_LISTENER_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=1772,
-    serialized_end=1926,
+    serialized_start=1802,
+    serialized_end=1956,
 )
 
 
@@ -1060,8 +1072,8 @@ _LIST_LISTENERS_REQ = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=1928,
-    serialized_end=1967,
+    serialized_start=1958,
+    serialized_end=1997,
 )
 
 
@@ -1109,8 +1121,8 @@ _LIST_SUBSYSTEMS_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=1969,
-    serialized_end=2082,
+    serialized_start=1999,
+    serialized_end=2112,
 )
 
 
@@ -1134,8 +1146,8 @@ _GET_SUBSYSTEMS_REQ = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=2084,
-    serialized_end=2104,
+    serialized_start=2114,
+    serialized_end=2134,
 )
 
 
@@ -1159,8 +1171,8 @@ _GET_SPDK_NVMF_LOG_FLAGS_AND_LEVEL_REQ = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=2106,
-    serialized_end=2145,
+    serialized_start=2136,
+    serialized_end=2175,
 )
 
 
@@ -1184,8 +1196,8 @@ _DISABLE_SPDK_NVMF_LOGS_REQ = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=2147,
-    serialized_end=2175,
+    serialized_start=2177,
+    serialized_end=2205,
 )
 
 
@@ -1233,8 +1245,8 @@ _SET_SPDK_NVMF_LOGS_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=2177,
-    serialized_end=2303,
+    serialized_start=2207,
+    serialized_end=2333,
 )
 
 
@@ -1270,8 +1282,8 @@ _GET_GATEWAY_INFO_REQ = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=2305,
-    serialized_end=2369,
+    serialized_start=2335,
+    serialized_end=2399,
 )
 
 
@@ -1309,8 +1321,8 @@ _ANA_GROUP_STATE = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=2371,
-    serialized_end=2431,
+    serialized_start=2401,
+    serialized_end=2461,
 )
 
 
@@ -1348,8 +1360,8 @@ _NQN_ANA_STATES = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=2433,
-    serialized_end=2496,
+    serialized_start=2463,
+    serialized_end=2526,
 )
 
 
@@ -1380,8 +1392,8 @@ _ANA_INFO = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=2498,
-    serialized_end=2541,
+    serialized_start=2528,
+    serialized_end=2571,
 )
 
 
@@ -1419,8 +1431,8 @@ _REQ_STATUS = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=2543,
-    serialized_end=2594,
+    serialized_start=2573,
+    serialized_end=2624,
 )
 
 
@@ -1465,8 +1477,8 @@ _NSID_STATUS = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=2596,
-    serialized_end=2662,
+    serialized_start=2626,
+    serialized_end=2692,
 )
 
 
@@ -1497,8 +1509,8 @@ _SUBSYSTEMS_INFO = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=2664,
-    serialized_end=2713,
+    serialized_start=2694,
+    serialized_end=2743,
 )
 
 
@@ -1624,8 +1636,8 @@ _SUBSYSTEM = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=2716,
-    serialized_end=3096,
+    serialized_start=2746,
+    serialized_end=3126,
 )
 
 
@@ -1689,8 +1701,8 @@ _LISTEN_ADDRESS = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=3098,
-    serialized_end=3217,
+    serialized_start=3128,
+    serialized_end=3247,
 )
 
 
@@ -1788,8 +1800,8 @@ _NAMESPACE = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=3220,
-    serialized_end=3421,
+    serialized_start=3250,
+    serialized_end=3451,
 )
 
 
@@ -1834,8 +1846,8 @@ _SUBSYSTEMS_INFO_CLI = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=3423,
-    serialized_end=3519,
+    serialized_start=3453,
+    serialized_end=3549,
 )
 
 
@@ -1903,6 +1915,13 @@ _SUBSYSTEM_CLI = _descriptor.Descriptor(
             message_type=None, enum_type=None, containing_type=None,
             is_extension=False, extension_scope=None,
             serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+        _descriptor.FieldDescriptor(
+            name='max_namespaces', full_name='subsystem_cli.max_namespaces', index=8,
+            number=9, type=13, cpp_type=3, label=1,
+            has_default_value=False, default_value=0,
+            message_type=None, enum_type=None, containing_type=None,
+            is_extension=False, extension_scope=None,
+            serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     ],
     extensions=[
     ],
@@ -1915,8 +1934,8 @@ _SUBSYSTEM_CLI = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=3522,
-    serialized_end=3696,
+    serialized_start=3552,
+    serialized_end=3750,
 )
 
 
@@ -1998,6 +2017,13 @@ _GATEWAY_INFO = _descriptor.Descriptor(
             message_type=None, enum_type=None, containing_type=None,
             is_extension=False, extension_scope=None,
             serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
+        _descriptor.FieldDescriptor(
+            name='load_balancing_group', full_name='gateway_info.load_balancing_group', index=10,
+            number=11, type=13, cpp_type=3, label=1,
+            has_default_value=False, default_value=0,
+            message_type=None, enum_type=None, containing_type=None,
+            is_extension=False, extension_scope=None,
+            serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
     ],
     extensions=[
     ],
@@ -2015,8 +2041,8 @@ _GATEWAY_INFO = _descriptor.Descriptor(
             create_key=_descriptor._internal_create_key,
             fields=[]),
     ],
-    serialized_start=3699,
-    serialized_end=3912,
+    serialized_start=3753,
+    serialized_end=3996,
 )
 
 
@@ -2061,8 +2087,8 @@ _CLI_VERSION = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=3914,
-    serialized_end=3983,
+    serialized_start=3998,
+    serialized_end=4067,
 )
 
 
@@ -2107,8 +2133,8 @@ _GW_VERSION = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=3985,
-    serialized_end=4053,
+    serialized_start=4069,
+    serialized_end=4137,
 )
 
 
@@ -2167,8 +2193,8 @@ _LISTENER_INFO = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=4055,
-    serialized_end=4173,
+    serialized_start=4139,
+    serialized_end=4257,
 )
 
 
@@ -2213,8 +2239,8 @@ _LISTENERS_INFO = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=4175,
-    serialized_end=4265,
+    serialized_start=4259,
+    serialized_end=4349,
 )
 
 
@@ -2245,8 +2271,8 @@ _HOST = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=4267,
-    serialized_end=4286,
+    serialized_start=4351,
+    serialized_end=4370,
 )
 
 
@@ -2305,8 +2331,8 @@ _HOSTS_INFO = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=4288,
-    serialized_end=4408,
+    serialized_start=4372,
+    serialized_end=4492,
 )
 
 
@@ -2386,8 +2412,8 @@ _CONNECTION = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=4411,
-    serialized_end=4581,
+    serialized_start=4495,
+    serialized_end=4665,
 )
 
 
@@ -2439,8 +2465,8 @@ _CONNECTIONS_INFO = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=4583,
-    serialized_end=4697,
+    serialized_start=4667,
+    serialized_end=4781,
 )
 
 
@@ -2548,8 +2574,8 @@ _NAMESPACE_CLI = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=4700,
-    serialized_end=4998,
+    serialized_start=4784,
+    serialized_end=5082,
 )
 
 
@@ -2601,8 +2627,8 @@ _NAMESPACES_INFO = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=5000,
-    serialized_end=5115,
+    serialized_start=5084,
+    serialized_end=5199,
 )
 
 
@@ -2815,8 +2841,8 @@ _NAMESPACE_IO_STATS_INFO = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=5118,
-    serialized_end=5813,
+    serialized_start=5202,
+    serialized_end=5897,
 )
 
 
@@ -2854,8 +2880,8 @@ _SPDK_LOG_FLAG_INFO = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=5815,
-    serialized_end=5866,
+    serialized_start=5899,
+    serialized_end=5950,
 )
 
 
@@ -2914,8 +2940,8 @@ _SPDK_NVMF_LOG_FLAGS_AND_LEVEL_INFO = _descriptor.Descriptor(
     extension_ranges=[],
     oneofs=[
     ],
-    serialized_start=5869,
-    serialized_end=6055,
+    serialized_start=5953,
+    serialized_end=6139,
 )
 
 _NAMESPACE_ADD_REQ.oneofs_by_name['_nsid'].fields.append(
@@ -2933,6 +2959,9 @@ _NAMESPACE_ADD_REQ.fields_by_name['create_image'].containing_oneof = _NAMESPACE_
 _NAMESPACE_ADD_REQ.oneofs_by_name['_size'].fields.append(
     _NAMESPACE_ADD_REQ.fields_by_name['size'])
 _NAMESPACE_ADD_REQ.fields_by_name['size'].containing_oneof = _NAMESPACE_ADD_REQ.oneofs_by_name['_size']
+_NAMESPACE_ADD_REQ.oneofs_by_name['_force'].fields.append(
+    _NAMESPACE_ADD_REQ.fields_by_name['force'])
+_NAMESPACE_ADD_REQ.fields_by_name['force'].containing_oneof = _NAMESPACE_ADD_REQ.oneofs_by_name['_force']
 _NAMESPACE_RESIZE_REQ.oneofs_by_name['_nsid'].fields.append(
     _NAMESPACE_RESIZE_REQ.fields_by_name['nsid'])
 _NAMESPACE_RESIZE_REQ.fields_by_name['nsid'].containing_oneof = _NAMESPACE_RESIZE_REQ.oneofs_by_name['_nsid']
@@ -3462,8 +3491,8 @@ _GATEWAY = _descriptor.ServiceDescriptor(
     index=0,
     serialized_options=None,
     create_key=_descriptor._internal_create_key,
-    serialized_start=6240,
-    serialized_end=7690,
+    serialized_start=6324,
+    serialized_end=7774,
     methods=[
         _descriptor.MethodDescriptor(
             name='namespace_add',