From c53641659a7d2bc1c58ad02bad57edc8be97a4fb Mon Sep 17 00:00:00 2001 From: Manohar Reddy Date: Mon, 8 Jun 2026 10:43:40 +0200 Subject: [PATCH 1/2] POST volume: return 422 when invalid node ID is passed --- simplyblock_web/api/v2/cluster/storage_pool/volume/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplyblock_web/api/v2/cluster/storage_pool/volume/__init__.py b/simplyblock_web/api/v2/cluster/storage_pool/volume/__init__.py index a2dbdedc0f..67fed213ee 100644 --- a/simplyblock_web/api/v2/cluster/storage_pool/volume/__init__.py +++ b/simplyblock_web/api/v2/cluster/storage_pool/volume/__init__.py @@ -113,7 +113,7 @@ def add( raise AssertionError('unreachable') if volume_id_or_false == False: # noqa - raise ValueError(error) + raise HTTPException(422, error) return util.creation_response( request, response_format, From 5934b6465cb307442208e60206f596951dbce2b7 Mon Sep 17 00:00:00 2001 From: Manohar Reddy Date: Mon, 3 Aug 2026 10:13:01 +0200 Subject: [PATCH 2/2] POST volume: return 404 when storage node is not found add_lvol_ha now raises KeyError for an unresolvable host node instead of returning it as a generic (False, error) tuple, so the API layer can tell a missing-resource lookup apart from an ordinary validation failure and map it to 404 instead of 422. --- .../controllers/lvol_controller.py | 2 +- .../cluster/storage_pool/volume/__init__.py | 53 ++++++++++--------- .../unit/web/api/v2/test_volume_endpoints.py | 10 ++++ 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/simplyblock_core/controllers/lvol_controller.py b/simplyblock_core/controllers/lvol_controller.py index 5db9b88409..1554250fb2 100755 --- a/simplyblock_core/controllers/lvol_controller.py +++ b/simplyblock_core/controllers/lvol_controller.py @@ -416,7 +416,7 @@ def add_lvol_ha(name, size, host_id_or_name, ha_type, pool_id_or_name, use_comp= if len(nodes) > 0: host_node = nodes[0] else: - return False, f"Can not find storage node: {host_id_or_name}" + raise KeyError(f"Can not find storage node: {host_id_or_name}") if host_node.lvol_sync_del(): logger.info(f"LVol sync delete task on node: {host_node.get_id()}, proceeding anyway") diff --git a/simplyblock_web/api/v2/cluster/storage_pool/volume/__init__.py b/simplyblock_web/api/v2/cluster/storage_pool/volume/__init__.py index 67fed213ee..5fb9e88c96 100644 --- a/simplyblock_web/api/v2/cluster/storage_pool/volume/__init__.py +++ b/simplyblock_web/api/v2/cluster/storage_pool/volume/__init__.py @@ -75,31 +75,34 @@ def add( pass if isinstance(data, _CreateParams): - volume_id_or_false, error = lvol_controller.add_lvol_ha( - name=data.name, - size=data.size, - pool_id_or_name=pool.get_id(), - use_crypto=data.encrypt, - max_size=0, - max_rw_iops=data.max_rw_iops, - max_rw_mbytes=data.max_rw_mbytes, - max_r_mbytes=data.max_r_mbytes, - max_w_mbytes=data.max_w_mbytes, - host_id_or_name=data.host_id, - ha_type=data.ha_type if data.ha_type is not None else 'default', - use_comp=False, - distr_vuid=0, - lvol_priority_class=data.priority_class, - namespaced=data.namespaced, - pvc_name=data.pvc_name, - ndcs=data.ndcs, - npcs=data.npcs, - allowed_hosts=data.allowed_hosts, - fabric=data.fabric, - max_namespace_per_subsys=data.max_namespace_per_subsys, - do_replicate=data.do_replicate, - replication_cluster_id=data.replication_cluster_id, - ) + try: + volume_id_or_false, error = lvol_controller.add_lvol_ha( + name=data.name, + size=data.size, + pool_id_or_name=pool.get_id(), + use_crypto=data.encrypt, + max_size=0, + max_rw_iops=data.max_rw_iops, + max_rw_mbytes=data.max_rw_mbytes, + max_r_mbytes=data.max_r_mbytes, + max_w_mbytes=data.max_w_mbytes, + host_id_or_name=data.host_id, + ha_type=data.ha_type if data.ha_type is not None else 'default', + use_comp=False, + distr_vuid=0, + lvol_priority_class=data.priority_class, + namespaced=data.namespaced, + pvc_name=data.pvc_name, + ndcs=data.ndcs, + npcs=data.npcs, + allowed_hosts=data.allowed_hosts, + fabric=data.fabric, + max_namespace_per_subsys=data.max_namespace_per_subsys, + do_replicate=data.do_replicate, + replication_cluster_id=data.replication_cluster_id, + ) + except KeyError as e: + raise HTTPException(404, str(e)) elif isinstance(data, _CloneParams): volume_id_or_false, error = snapshot_controller.clone( data.snapshot_id, diff --git a/tests/unit/web/api/v2/test_volume_endpoints.py b/tests/unit/web/api/v2/test_volume_endpoints.py index ca12385f34..cd8b29d118 100644 --- a/tests/unit/web/api/v2/test_volume_endpoints.py +++ b/tests/unit/web/api/v2/test_volume_endpoints.py @@ -78,6 +78,16 @@ def test_clone_parameters_call_snapshot_clone(self, client, db, pool, lvol_contr ) lvol_controller.add_lvol_ha.assert_not_called() + def test_missing_host_node_returns_404(self, client, db, pool, lvol_controller): + db.get_lvol_by_name.side_effect = KeyError('LVol not found') + lvol_controller.add_lvol_ha.side_effect = KeyError('Can not find storage node: bogus-id') + + response = client.post( + f'{BASE}/', json={'name': 'volume-1', 'size': '10G', 'host_id': 'bogus-id'}, + ) + + assert response.status_code == 404 + def test_existing_name_returns_409(self, client, db, pool, volume, lvol_controller): db.get_lvol_by_name.return_value = volume