From 4adf48998d97f8199bcb86c8d8994bb5989c198a Mon Sep 17 00:00:00 2001 From: Ernesto Puerta Date: Wed, 13 Mar 2024 14:06:10 +0100 Subject: [PATCH] mgr/dashboard: fix NVMeoF API * 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 --- .../mgr/dashboard/controllers/__init__.py | 3 +- src/pybind/mgr/dashboard/controllers/_docs.py | 9 +- .../mgr/dashboard/controllers/_endpoint.py | 8 +- .../dashboard/controllers/_rest_controller.py | 4 +- src/pybind/mgr/dashboard/controllers/auth.py | 1 + src/pybind/mgr/dashboard/controllers/docs.py | 6 +- .../mgr/dashboard/controllers/nvmeof.py | 519 ++++++++++++------ src/pybind/mgr/dashboard/model/nvmeof.py | 92 ++++ src/pybind/mgr/dashboard/openapi.yaml | 444 +++++++++++---- src/pybind/mgr/dashboard/requirements.txt | 2 +- .../mgr/dashboard/services/exception.py | 9 +- .../mgr/dashboard/services/nvmeof_client.py | 223 ++++---- .../dashboard/services/proto/gateway.proto | 8 +- .../dashboard/services/proto/gateway_pb2.py | 237 ++++---- 14 files changed, 1072 insertions(+), 493 deletions(-) create mode 100644 src/pybind/mgr/dashboard/model/nvmeof.py diff --git a/src/pybind/mgr/dashboard/controllers/__init__.py b/src/pybind/mgr/dashboard/controllers/__init__.py index af3f276ebfa2f..3db5da5d32359 100755 --- a/src/pybind/mgr/dashboard/controllers/__init__.py +++ b/src/pybind/mgr/dashboard/controllers/__init__.py @@ -2,7 +2,7 @@ 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 @@ 'Task', 'ControllerAuthMixin', 'EndpointDoc', + 'Param', 'APIDoc', 'allow_empty_body', 'ENDPOINT_MAP', diff --git a/src/pybind/mgr/dashboard/controllers/_docs.py b/src/pybind/mgr/dashboard/controllers/_docs.py index 5bd7a5a7a6ea5..7301875f6b4f8 100644 --- a/src/pybind/mgr/dashboard/controllers/_docs.py +++ b/src/pybind/mgr/dashboard/controllers/_docs.py @@ -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 @@ def __call__(self, func: Any) -> Any: 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 diff --git a/src/pybind/mgr/dashboard/controllers/_endpoint.py b/src/pybind/mgr/dashboard/controllers/_endpoint.py index fccab89c34979..6d77dcdcea4aa 100644 --- a/src/pybind/mgr/dashboard/controllers/_endpoint.py +++ b/src/pybind/mgr/dashboard/controllers/_endpoint.py @@ -12,9 +12,9 @@ def __init__(self, method=None, path=None, path_params=None, query_params=None, 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 @@ def __init__(self, method=None, path=None, path_params=None, query_params=None, " 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 @@ def __init__(self, method=None, path=None, path_params=None, query_params=None, 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']: diff --git a/src/pybind/mgr/dashboard/controllers/_rest_controller.py b/src/pybind/mgr/dashboard/controllers/_rest_controller.py index 0224c366f3bca..c3bd07cf807b7 100644 --- a/src/pybind/mgr/dashboard/controllers/_rest_controller.py +++ b/src/pybind/mgr/dashboard/controllers/_rest_controller.py @@ -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 diff --git a/src/pybind/mgr/dashboard/controllers/auth.py b/src/pybind/mgr/dashboard/controllers/auth.py index e8bb4bbef8e15..eca87a9a84edb 100644 --- a/src/pybind/mgr/dashboard/controllers/auth.py +++ b/src/pybind/mgr/dashboard/controllers/auth.py @@ -38,6 +38,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) user_perms, pwd_expiration_date, pwd_update_required = None, None, None diff --git a/src/pybind/mgr/dashboard/controllers/docs.py b/src/pybind/mgr/dashboard/controllers/docs.py index 2ade4ef9bad4c..9ecb513621a10 100644 --- a/src/pybind/mgr/dashboard/controllers/docs.py +++ b/src/pybind/mgr/dashboard/controllers/docs.py @@ -215,7 +215,7 @@ def _gen_responses(cls, method, resp_object=None, 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 @@ def set_request_body_param(cls, endpoint_param, method, methods, p_info): @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 @@ def gen_paths(cls, all_endpoints): 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) diff --git a/src/pybind/mgr/dashboard/controllers/nvmeof.py b/src/pybind/mgr/dashboard/controllers/nvmeof.py index b485a5aa17b22..a3b4925b0eeee 100644 --- a/src/pybind/mgr/dashboard/controllers/nvmeof.py +++ b/src/pybind/mgr/dashboard/controllers/nvmeof.py @@ -1,167 +1,370 @@ # -*- 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 index 0000000000000..543ae6afdddeb --- /dev/null +++ b/src/pybind/mgr/dashboard/model/nvmeof.py @@ -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 diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index c59532c7243d6..de3a9df0338c2 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -7995,7 +7995,7 @@ paths: summary: Updates an NFS-Ganesha export tags: - NFS-Ganesha - /api/nvmeof/gateway/info: + /api/nvmeof/gateway: get: parameters: [] responses: @@ -8015,10 +8015,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: @@ -8026,15 +8048,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': @@ -8058,15 +8084,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 @@ -8089,17 +8212,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 @@ -8129,13 +8296,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 @@ -8158,26 +8325,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': @@ -8203,8 +8381,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 @@ -8225,6 +8403,11 @@ paths: name: traddr schema: type: string + - default: 4420 + in: query + name: trsvcid + schema: + type: integer responses: '202': content: @@ -8249,10 +8432,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: @@ -8266,23 +8483,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': @@ -8308,13 +8524,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 @@ -8342,12 +8564,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 @@ -8368,62 +8596,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': @@ -8435,35 +8654,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': @@ -8475,9 +8689,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: [] @@ -14373,7 +14587,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 diff --git a/src/pybind/mgr/dashboard/requirements.txt b/src/pybind/mgr/dashboard/requirements.txt index f7fc52bf92d82..b5c78ac8bec40 100644 --- a/src/pybind/mgr/dashboard/requirements.txt +++ b/src/pybind/mgr/dashboard/requirements.txt @@ -6,7 +6,7 @@ requests Routes -e ../../../python-common prettytable -pytest==8.0.2 +pytest==7.0.1 pyyaml natsort setuptools diff --git a/src/pybind/mgr/dashboard/services/exception.py b/src/pybind/mgr/dashboard/services/exception.py index c39209569dbd7..e8e019b0f71e6 100644 --- a/src/pybind/mgr/dashboard/services/exception.py +++ b/src/pybind/mgr/dashboard/services/exception.py @@ -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') diff --git a/src/pybind/mgr/dashboard/services/nvmeof_client.py b/src/pybind/mgr/dashboard/services/nvmeof_client.py index c51d7e80edb70..5dee7dfcfbc3b 100644 --- a/src/pybind/mgr/dashboard/services/nvmeof_client.py +++ b/src/pybind/mgr/dashboard/services/nvmeof_client.py @@ -1,121 +1,134 @@ +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 diff --git a/src/pybind/mgr/dashboard/services/proto/gateway.proto b/src/pybind/mgr/dashboard/services/proto/gateway.proto index 81e2b7205ceb1..b2698872a90ca 100644 --- a/src/pybind/mgr/dashboard/services/proto/gateway.proto +++ b/src/pybind/mgr/dashboard/services/proto/gateway.proto @@ -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 +} diff --git a/src/pybind/mgr/dashboard/services/proto/gateway_pb2.py b/src/pybind/mgr/dashboard/services/proto/gateway_pb2.py index 6bd40aaee4739..6d807fc27a76b 100644 --- a/src/pybind/mgr/dashboard/services/proto/gateway_pb2.py +++ b/src/pybind/mgr/dashboard/services/proto/gateway_pb2.py @@ -19,7 +19,7 @@ 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 @@ ], 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 @@ ], 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 @@ ], 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 @@ 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 @@ 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 @@ 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 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=293, - serialized_end=412, + serialized_start=323, + serialized_end=442, ) @@ -365,8 +377,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=414, - serialized_end=521, + serialized_start=444, + serialized_end=551, ) @@ -469,8 +481,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=524, - serialized_end=856, + serialized_start=554, + serialized_end=886, ) @@ -532,8 +544,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=859, - serialized_end=999, + serialized_start=889, + serialized_end=1029, ) @@ -588,8 +600,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=1001, - serialized_end=1102, + serialized_start=1031, + serialized_end=1132, ) @@ -646,8 +658,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=1105, - serialized_end=1240, + serialized_start=1135, + serialized_end=1270, ) @@ -690,8 +702,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=1242, - serialized_end=1317, + serialized_start=1272, + serialized_end=1347, ) @@ -746,8 +758,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=1319, - serialized_end=1415, + serialized_start=1349, + serialized_end=1445, ) @@ -785,8 +797,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1417, - serialized_end=1472, + serialized_start=1447, + serialized_end=1502, ) @@ -824,8 +836,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1474, - serialized_end=1532, + serialized_start=1504, + serialized_end=1562, ) @@ -856,8 +868,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1534, - serialized_end=1569, + serialized_start=1564, + serialized_end=1599, ) @@ -888,8 +900,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1571, - serialized_end=1612, + serialized_start=1601, + serialized_end=1642, ) @@ -958,8 +970,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=1615, - serialized_end=1769, + serialized_start=1645, + serialized_end=1799, ) @@ -1028,8 +1040,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=1772, - serialized_end=1926, + serialized_start=1802, + serialized_end=1956, ) @@ -1060,8 +1072,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1928, - serialized_end=1967, + serialized_start=1958, + serialized_end=1997, ) @@ -1109,8 +1121,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=1969, - serialized_end=2082, + serialized_start=1999, + serialized_end=2112, ) @@ -1134,8 +1146,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2084, - serialized_end=2104, + serialized_start=2114, + serialized_end=2134, ) @@ -1159,8 +1171,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2106, - serialized_end=2145, + serialized_start=2136, + serialized_end=2175, ) @@ -1184,8 +1196,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2147, - serialized_end=2175, + serialized_start=2177, + serialized_end=2205, ) @@ -1233,8 +1245,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=2177, - serialized_end=2303, + serialized_start=2207, + serialized_end=2333, ) @@ -1270,8 +1282,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=2305, - serialized_end=2369, + serialized_start=2335, + serialized_end=2399, ) @@ -1309,8 +1321,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2371, - serialized_end=2431, + serialized_start=2401, + serialized_end=2461, ) @@ -1348,8 +1360,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2433, - serialized_end=2496, + serialized_start=2463, + serialized_end=2526, ) @@ -1380,8 +1392,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2498, - serialized_end=2541, + serialized_start=2528, + serialized_end=2571, ) @@ -1419,8 +1431,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2543, - serialized_end=2594, + serialized_start=2573, + serialized_end=2624, ) @@ -1465,8 +1477,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2596, - serialized_end=2662, + serialized_start=2626, + serialized_end=2692, ) @@ -1497,8 +1509,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2664, - serialized_end=2713, + serialized_start=2694, + serialized_end=2743, ) @@ -1624,8 +1636,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=2716, - serialized_end=3096, + serialized_start=2746, + serialized_end=3126, ) @@ -1689,8 +1701,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=3098, - serialized_end=3217, + serialized_start=3128, + serialized_end=3247, ) @@ -1788,8 +1800,8 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=3220, - serialized_end=3421, + serialized_start=3250, + serialized_end=3451, ) @@ -1834,8 +1846,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3423, - serialized_end=3519, + serialized_start=3453, + serialized_end=3549, ) @@ -1903,6 +1915,13 @@ 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 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3522, - serialized_end=3696, + serialized_start=3552, + serialized_end=3750, ) @@ -1998,6 +2017,13 @@ 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 @@ create_key=_descriptor._internal_create_key, fields=[]), ], - serialized_start=3699, - serialized_end=3912, + serialized_start=3753, + serialized_end=3996, ) @@ -2061,8 +2087,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3914, - serialized_end=3983, + serialized_start=3998, + serialized_end=4067, ) @@ -2107,8 +2133,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=3985, - serialized_end=4053, + serialized_start=4069, + serialized_end=4137, ) @@ -2167,8 +2193,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4055, - serialized_end=4173, + serialized_start=4139, + serialized_end=4257, ) @@ -2213,8 +2239,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4175, - serialized_end=4265, + serialized_start=4259, + serialized_end=4349, ) @@ -2245,8 +2271,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4267, - serialized_end=4286, + serialized_start=4351, + serialized_end=4370, ) @@ -2305,8 +2331,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4288, - serialized_end=4408, + serialized_start=4372, + serialized_end=4492, ) @@ -2386,8 +2412,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4411, - serialized_end=4581, + serialized_start=4495, + serialized_end=4665, ) @@ -2439,8 +2465,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4583, - serialized_end=4697, + serialized_start=4667, + serialized_end=4781, ) @@ -2548,8 +2574,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=4700, - serialized_end=4998, + serialized_start=4784, + serialized_end=5082, ) @@ -2601,8 +2627,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5000, - serialized_end=5115, + serialized_start=5084, + serialized_end=5199, ) @@ -2815,8 +2841,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5118, - serialized_end=5813, + serialized_start=5202, + serialized_end=5897, ) @@ -2854,8 +2880,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=5815, - serialized_end=5866, + serialized_start=5899, + serialized_end=5950, ) @@ -2914,8 +2940,8 @@ 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.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 @@ 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',