Copilot commented on code in PR #12966:
URL: https://github.com/apache/cloudstack/pull/12966#discussion_r3043138158


##########
api/src/main/java/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java:
##########
@@ -153,6 +160,13 @@ private Long getProjectId() {
         return projectId;
     }
 
+    public Long getStorageId() {
+        if (snapshotId != null && storageId != null) {
+            throw new IllegalArgumentException("StorageId parameter cannot be 
specified with the SnapshotId parameter.");
+        }
+        return storageId;
+    }

Review Comment:
   The mutual-exclusion check for snapshotId vs storageId is currently 
ineffective: when snapshotId is provided, createVolume() never calls 
getStorageId(), so a request with both parameters will silently ignore 
storageid and proceed down the snapshot path.
   
   To ensure the API contract is enforced, validate this combination in a code 
path that always runs (e.g., in CreateVolumeCmd.create()/execute() or in 
VolumeApiServiceImpl.allocVolume/createVolume before branching) and throw an 
InvalidParameterValueException/ServerApiException rather than letting the 
conflict slip through.



##########
api/src/main/java/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java:
##########
@@ -109,6 +110,12 @@ public class CreateVolumeCmd extends 
BaseAsyncCreateCustomIdCmd implements UserC
                description = "The ID of the Instance; to be used with snapshot 
Id, Instance to which the volume gets attached after creation")
     private Long virtualMachineId;
 
+    @Parameter(name = ApiConstants.STORAGE_ID,
+            type = CommandType.UUID,
+            entityType = StoragePoolResponse.class,
+            description = "Storage pool ID to create the volume in. Exclusive 
with SnapshotId parameter.")

Review Comment:
   storageid is a user-exposed parameter here, but there’s no indication it’s 
restricted (no authorized=Admin) and the backend path in VolumeApiServiceImpl 
doesn’t enforce pool-level access/compatibility checks. If this is intended 
primarily for admin/migration workflows, consider restricting the parameter to 
admins (as done in other user commands that expose storage pool IDs) or ensure 
the service layer performs full access checks before acting on the provided 
pool ID.
   ```suggestion
               description = "Storage pool ID to create the volume in. 
Exclusive with SnapshotId parameter.",
               authorized = {RoleType.Admin})
   ```



##########
server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java:
##########
@@ -1074,6 +1085,8 @@ public VolumeVO createVolume(CreateVolumeCmd cmd) {
                         throw new CloudRuntimeException(message.toString());
                     }
                 }
+            } else if (cmd.getStorageId() != null) {
+                volume = allocateVolumeOnStorage(cmd.getEntityId(), 
cmd.getStorageId());

Review Comment:
   New storageid-driven creation path in createVolume()/allocateVolumeOnStorage 
isn’t covered by existing unit tests (there are tests for VolumeApiServiceImpl 
in server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java, but 
none exercise this new branch). Adding a unit test that verifies pool 
validation + volService.createVolumeAsync invocation and failure handling would 
help prevent regressions.



##########
server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java:
##########
@@ -1043,6 +1043,17 @@ public boolean validateVolumeSizeInBytes(long size) {
         return true;
     }
 
+    private VolumeVO allocateVolumeOnStorage(Long volumeId, Long storageId) 
throws ExecutionException, InterruptedException {
+        DataStore destStore = dataStoreMgr.getDataStore(storageId, 
DataStoreRole.Primary);
+        VolumeInfo destVolume = volFactory.getVolume(volumeId, destStore);
+        AsyncCallFuture<VolumeApiResult> createVolumeFuture = 
volService.createVolumeAsync(destVolume, destStore);
+        VolumeApiResult createVolumeResult = createVolumeFuture.get();
+        if (createVolumeResult.isFailed()) {
+            throw new CloudRuntimeException("Creation of a dest volume failed: 
" + createVolumeResult.getResult());
+        }
+        return _volsDao.findById(destVolume.getId());

Review Comment:
   allocateVolumeOnStorage() doesn’t validate that the provided storageId 
resolves to an existing/accessible primary storage pool 
(dataStoreMgr.getDataStore may return null) and doesn’t check pool conditions 
(e.g., in-maintenance/disabled, zone mismatch with the volume, storage access 
groups). As-is, this can lead to NPEs and also allows callers to force volume 
creation on arbitrary pools bypassing the normal allocation/compatibility 
checks.
   
   Consider resolving StoragePoolVO/PrimaryDataStore first, rejecting null / 
in-maintenance, validating the pool is in the expected state (Ready/Up) and in 
the same zone as the volume, and running the same compatibility checks used 
elsewhere (e.g., 
storageMgr.checkIfReadyVolumeFitsInStoragePoolWithStorageAccessGroups and 
doesStoragePoolSupportDiskOffering / capacity checks) before calling 
volService.createVolumeAsync.



##########
server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java:
##########
@@ -1043,6 +1043,17 @@ public boolean validateVolumeSizeInBytes(long size) {
         return true;
     }
 
+    private VolumeVO allocateVolumeOnStorage(Long volumeId, Long storageId) 
throws ExecutionException, InterruptedException {
+        DataStore destStore = dataStoreMgr.getDataStore(storageId, 
DataStoreRole.Primary);
+        VolumeInfo destVolume = volFactory.getVolume(volumeId, destStore);
+        AsyncCallFuture<VolumeApiResult> createVolumeFuture = 
volService.createVolumeAsync(destVolume, destStore);
+        VolumeApiResult createVolumeResult = createVolumeFuture.get();

Review Comment:
   allocateVolumeOnStorage() is a bit misleadingly named: it actually triggers 
backend creation (createVolumeAsync) and blocks waiting for completion. 
Renaming it to reflect the side-effect (e.g., createVolumeOnStoragePool) would 
make the control flow in createVolume() clearer and reduce confusion with 
allocVolume(), which only persists DB state.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to