From 8d8d1eefa1940302df08b1a929b749b11abfa0ee Mon Sep 17 00:00:00 2001 From: Manohar Reddy Date: Sat, 23 May 2026 13:42:33 +0200 Subject: [PATCH 1/2] exclude IN_DELETION volumes from lvol list and /connect APIs --- simplyblock_core/controllers/lvol_controller.py | 2 ++ simplyblock_core/db_controller.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/simplyblock_core/controllers/lvol_controller.py b/simplyblock_core/controllers/lvol_controller.py index 88d600904..afb59af8e 100755 --- a/simplyblock_core/controllers/lvol_controller.py +++ b/simplyblock_core/controllers/lvol_controller.py @@ -1755,6 +1755,8 @@ def connect_lvol(uuid, ctrl_loss_tmo=constants.LVOL_NVME_CONNECT_CTRL_LOSS_TMO, lvol = db_controller.get_lvol_by_id(uuid) if lvol.status == LVol.STATUS_DELETED: raise KeyError(f"LVol {uuid} is deleted") + if lvol.status == LVol.STATUS_IN_DELETION: + raise KeyError(f"LVol {uuid} is being deleted") except KeyError: logger.exception("Failed to get lvol by id: %s", uuid) return False, "Failed to find volume" diff --git a/simplyblock_core/db_controller.py b/simplyblock_core/db_controller.py index 03ff2d0d5..06c543652 100644 --- a/simplyblock_core/db_controller.py +++ b/simplyblock_core/db_controller.py @@ -124,7 +124,7 @@ def get_pool_by_name(self, name) -> Pool: def get_lvols(self, cluster_id=None) -> List[LVol]: lvols = self.get_all_lvols() - lvols = [lvol for lvol in lvols if lvol.status != LVol.STATUS_DELETED] + lvols = [lvol for lvol in lvols if lvol.status not in (LVol.STATUS_DELETED, LVol.STATUS_IN_DELETION)] if not cluster_id: return lvols From 017f2bf6cb27951bb3918266034562194b72161b Mon Sep 17 00:00:00 2001 From: Manohar Reddy Date: Sat, 23 May 2026 14:51:52 +0200 Subject: [PATCH 2/2] handle IN_DELETION state in volume create and clone paths --- .../controllers/snapshot_controller.py | 21 ++++++++++++------- simplyblock_core/db_controller.py | 12 ++++++++++- simplyblock_core/models/lvol_model.py | 4 ++++ simplyblock_web/api/v1/lvol.py | 12 +++++++---- simplyblock_web/api/v2/volume.py | 6 ++++-- 5 files changed, 40 insertions(+), 15 deletions(-) diff --git a/simplyblock_core/controllers/snapshot_controller.py b/simplyblock_core/controllers/snapshot_controller.py index 46bcfaf7b..8cc65708e 100644 --- a/simplyblock_core/controllers/snapshot_controller.py +++ b/simplyblock_core/controllers/snapshot_controller.py @@ -16,7 +16,7 @@ from simplyblock_core.models.job_schedule import JobSchedule from simplyblock_core.models.pool import Pool from simplyblock_core.models.snapshot import SnapShot -from simplyblock_core.models.lvol_model import LVol +from simplyblock_core.models.lvol_model import LVol, LVolInDeletionError from simplyblock_core.models.storage_node import StorageNode @@ -613,15 +613,20 @@ def clone(snapshot_id, clone_name, new_size=0, pvc_name=None, pvc_namespace=None logger.error(msg) return False, msg - for lvol in db_controller.get_lvols(): - if lvol.pool_uuid != pool.get_id() or lvol.lvol_name != clone_name: - continue - if lvol.cloned_from_snap == snapshot_id and lvol.status != LVol.STATUS_IN_DELETION: - logger.info(f"Clone already exists, reusing lvol: {lvol.get_id()}") - return lvol.get_id(), False - msg=f"LVol name must be unique: {clone_name}" + try: + existing = db_controller.get_lvol_by_pool_and_name(pool.get_id(), clone_name) + if existing.cloned_from_snap == snapshot_id: + logger.info(f"Clone already exists, reusing lvol: {existing.get_id()}") + return existing.get_id(), False + msg = f"LVol name must be unique: {clone_name}" + logger.error(msg) + return False, msg + except LVolInDeletionError: + msg = f"Clone {clone_name} is being deleted, retry later" logger.error(msg) return False, msg + except KeyError: + pass size = snap.size if 0 < pool.lvol_max_size < size: diff --git a/simplyblock_core/db_controller.py b/simplyblock_core/db_controller.py index 06c543652..377225b00 100644 --- a/simplyblock_core/db_controller.py +++ b/simplyblock_core/db_controller.py @@ -10,7 +10,7 @@ from simplyblock_core.models.cluster import Cluster from simplyblock_core.models.events import EventObj from simplyblock_core.models.job_schedule import JobSchedule -from simplyblock_core.models.lvol_model import LVol +from simplyblock_core.models.lvol_model import LVol, LVolInDeletionError from simplyblock_core.models.mgmt_node import MgmtNode from simplyblock_core.models.nvme_device import NVMeDevice, JMDevice from simplyblock_core.models.pool import Pool @@ -196,6 +196,16 @@ def get_lvol_by_name(self, lvol_name) -> LVol: return lvol raise KeyError(f'LVol {lvol_name} not found') + def get_lvol_by_pool_and_name(self, pool_uuid: str, lvol_name: str) -> LVol: + for lvol in self.get_all_lvols(): + if lvol.pool_uuid == pool_uuid and lvol.lvol_name == lvol_name: + if lvol.status == LVol.STATUS_DELETED: + continue + if lvol.status == LVol.STATUS_IN_DELETION: + raise LVolInDeletionError(f"LVol {lvol_name} is being deleted") + return lvol + raise KeyError(f"LVol {lvol_name} not found in pool {pool_uuid}") + def get_mgmt_node_by_id(self, id) -> MgmtNode: ret = MgmtNode().read_from_db(self.kv_store, id) if not ret: diff --git a/simplyblock_core/models/lvol_model.py b/simplyblock_core/models/lvol_model.py index e4d9ed486..29ab92b66 100644 --- a/simplyblock_core/models/lvol_model.py +++ b/simplyblock_core/models/lvol_model.py @@ -6,6 +6,10 @@ from simplyblock_core.models.nvme_device import NVMeDevice +class LVolInDeletionError(Exception): + pass + + class LVol(BaseModel): STATUS_IN_CREATION = 'in_creation' diff --git a/simplyblock_web/api/v1/lvol.py b/simplyblock_web/api/v1/lvol.py index 80fb385d4..6c0b88c17 100644 --- a/simplyblock_web/api/v1/lvol.py +++ b/simplyblock_web/api/v1/lvol.py @@ -12,6 +12,7 @@ from simplyblock_web import utils from simplyblock_core import db_controller, utils as core_utils +from simplyblock_core.models.lvol_model import LVolInDeletionError logger = logging.getLogger(__name__) @@ -128,10 +129,13 @@ def add_lvol(): if not pool: return utils.get_response(None, f"Pool not found: {pool_id_or_name}", 400) - for lvol in db.get_lvols(): # pass - if lvol.pool_uuid == pool.get_id(): - if lvol.lvol_name == name: - return utils.get_response(lvol.get_id()) + try: + existing = db.get_lvol_by_pool_and_name(pool.get_id(), name) + return utils.get_response(existing.get_id()) + except LVolInDeletionError: + return utils.get_response(None, f"Volume {name} is being deleted, retry later", 409) + except KeyError: + pass rw_iops = utils.get_int_value_or_default(cl_data, "max_rw_iops", 0) rw_mbytes = utils.get_int_value_or_default(cl_data, "max_rw_mbytes", 0) diff --git a/simplyblock_web/api/v2/volume.py b/simplyblock_web/api/v2/volume.py index 8c36f6fd8..ac83c0d47 100644 --- a/simplyblock_web/api/v2/volume.py +++ b/simplyblock_web/api/v2/volume.py @@ -7,7 +7,7 @@ from simplyblock_core.db_controller import DBController from simplyblock_core import utils as core_utils from simplyblock_core.controllers import lvol_controller, snapshot_controller -from simplyblock_core.models.lvol_model import LVol +from simplyblock_core.models.lvol_model import LVol, LVolInDeletionError from .cluster import Cluster from .pool import StoragePool @@ -66,8 +66,10 @@ def add( ) -> Response: data = parameters.root try: - db.get_lvol_by_name(data.name) + db.get_lvol_by_pool_and_name(pool.get_id(), data.name) raise HTTPException(409, f'Volume {data.name} exists') + except LVolInDeletionError: + raise HTTPException(409, f'Volume {data.name} is being deleted, retry later') except KeyError: pass