]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: split ns add to separate api and cli functions 64516/head
authorTomer Haskalovitch <tomer.haska@ibm.com>
Mon, 14 Jul 2025 18:53:30 +0000 (21:53 +0300)
committerTomer Haskalovitch <tomer.haska@ibm.com>
Wed, 16 Jul 2025 17:50:58 +0000 (20:50 +0300)
Signed-off-by: Tomer Haskalovitch <tomer.haska@ibm.com>
src/pybind/mgr/dashboard/controllers/nvmeof.py
src/pybind/mgr/dashboard/services/nvmeof_cli.py
src/pybind/mgr/dashboard/tests/test_nvmeof_cli.py

index 34517159533a2e93635760921365b867659f00dd..060c846474eaa094964988af9ea0980a310a3a6f 100644 (file)
@@ -9,7 +9,7 @@ from orchestrator import OrchestratorError
 from .. import mgr
 from ..model import nvmeof as model
 from ..security import Scope
-from ..services.nvmeof_cli import NvmeofCLICommand
+from ..services.nvmeof_cli import NvmeofCLICommand, convert_to_bytes
 from ..services.orchestrator import OrchClient
 from ..tools import str_to_bool
 from . import APIDoc, APIRouter, BaseController, CreatePermission, \
@@ -425,7 +425,6 @@ else:
                 )
             },
         )
-        @NvmeofCLICommand("nvmeof ns add", model.NamespaceCreation)
         @convert_to_model(model.NamespaceCreation)
         @handle_nvmeof_error
         def create(
@@ -463,6 +462,49 @@ else:
                 )
             )
 
+        @NvmeofCLICommand("nvmeof ns add", model.NamespaceCreation)
+        @convert_to_model(model.NamespaceCreation)
+        @handle_nvmeof_error
+        def create_cli(
+            self,
+            nqn: str,
+            rbd_image_name: str,
+            rbd_pool: str = "rbd",
+            create_image: Optional[bool] = False,
+            size: Optional[str] = None,
+            rbd_image_size: Optional[str] = None,
+            trash_image: Optional[bool] = False,
+            block_size: int = 512,
+            load_balancing_group: Optional[int] = None,
+            force: Optional[bool] = False,
+            no_auto_visible: Optional[bool] = False,
+            disable_auto_resize: Optional[bool] = False,
+            read_only: Optional[bool] = False,
+            gw_group: Optional[str] = None,
+            traddr: Optional[str] = None,
+        ):
+            size_b = rbd_image_size_b = None
+            if size:
+                size_b = convert_to_bytes(size, default_unit='MB')
+            if rbd_image_size:
+                rbd_image_size_b = convert_to_bytes(rbd_image_size, default_unit='MB')
+            return NVMeoFClient(gw_group=gw_group, traddr=traddr).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=rbd_image_size_b or size_b,
+                    trash_image=trash_image,
+                    anagrpid=load_balancing_group,
+                    force=force,
+                    no_auto_visible=no_auto_visible,
+                    disable_auto_resize=disable_auto_resize,
+                    read_only=read_only
+                )
+            )
+
         @ReadPermission
         @Endpoint('PUT', '{nsid}/set_qos')
         @EndpointDoc(
@@ -556,7 +598,6 @@ else:
                 "traddr": Param(str, "NVMeoF gateway address", True, None),
             },
         )
-        @NvmeofCLICommand("nvmeof ns resize", model=model.RequestStatus)
         @convert_to_model(model.RequestStatus)
         @handle_nvmeof_error
         def resize(
@@ -576,6 +617,28 @@ else:
                 )
             )
 
+        @NvmeofCLICommand("nvmeof ns resize", model=model.RequestStatus)
+        @convert_to_model(model.RequestStatus)
+        @handle_nvmeof_error
+        def resize_cli(
+            self,
+            nqn: str,
+            nsid: str,
+            rbd_image_size: str,
+            gw_group: Optional[str] = None,
+            traddr: Optional[str] = None
+        ):
+            if rbd_image_size:
+                rbd_image_size_b = convert_to_bytes(rbd_image_size, default_unit='MB')
+            mib = 1024 * 1024
+            rbd_image_size_mb = rbd_image_size_b // mib
+
+            return NVMeoFClient(gw_group=gw_group, traddr=traddr).stub.namespace_resize(
+                NVMeoFClient.pb2.namespace_resize_req(
+                    subsystem_nqn=nqn, nsid=int(nsid), new_size=rbd_image_size_mb
+                )
+            )
+
         @ReadPermission
         @Endpoint('PUT', '{nsid}/add_host')
         @EndpointDoc(
index f1887d86da2192143ea804a39d7627e104edde25..75a586a5f8f1f0f92cf1d207df13162662656cfa 100644 (file)
@@ -55,6 +55,33 @@ def remove_nvmeof_gateway(_, name: str, daemon_name: str = ''):
         return -errno.EINVAL, '', str(ex)
 
 
+MULTIPLES = ['', "K", "M", "G", "T", "P"]
+UNITS = {
+    f"{prefix}{suffix}": 1024 ** mult
+    for mult, prefix in enumerate(MULTIPLES)
+    for suffix in ['', 'B', 'iB']
+    if not (prefix == '' and suffix == 'iB')
+}
+
+
+def convert_to_bytes(size: Union[int, str], default_unit=None):
+    if isinstance(size, int):
+        number = size
+        size = str(size)
+    else:
+        num_str = ''.join(filter(str.isdigit, size))
+        number = int(num_str)
+    unit_str = ''.join(filter(str.isalpha, size))
+    if not unit_str:
+        if not default_unit:
+            raise ValueError("default unit was not provided")
+        unit_str = default_unit
+
+    if unit_str in UNITS:
+        return number * UNITS[unit_str]
+    raise ValueError(f"Invalid unit: {unit_str}")
+
+
 def convert_from_bytes(num_in_bytes):
     units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
     size = float(num_in_bytes)
index bceaa7f04c1275f7a8319391ff7166a7c7cbf41d..96b7f9ce2e352d4b72eeb4562b6f6c7812d8b556 100644 (file)
@@ -9,7 +9,7 @@ from mgr_module import CLICommand, HandleCommandResult
 
 from ..model.nvmeof import CliFlags, CliHeader
 from ..services.nvmeof_cli import AnnotatedDataTextOutputFormatter, \
-    NvmeofCLICommand, convert_from_bytes
+    NvmeofCLICommand, convert_from_bytes, convert_to_bytes
 from ..tests import CLICommandTestMixin
 
 
@@ -416,3 +416,16 @@ class TestConverFromBytes:
         assert convert_from_bytes(1048576) == '1MB'
         assert convert_from_bytes(123) == '123B'
         assert convert_from_bytes(5368709120) == '5GB'
+
+
+class TestConvertToBytes:
+    def test_valid_inputs(self):
+        assert convert_to_bytes('200MB') == 209715200
+        assert convert_to_bytes('1MB') == 1048576
+        assert convert_to_bytes('123B') == 123
+        assert convert_to_bytes('5GB') == 5368709120
+
+    def test_default_unit(self):
+        with pytest.raises(ValueError):
+            assert convert_to_bytes('5') == 5368709120
+        assert convert_to_bytes('5', default_unit='GB') == 5368709120