nvazquez commented on code in PR #13053:
URL: https://github.com/apache/cloudstack/pull/13053#discussion_r3377381328


##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java:
##########
@@ -158,8 +159,58 @@ public boolean connectPhysicalDisk(String volumeUuid, 
KVMStoragePool pool, Map<S
         return true;
     }
 
+    /**
+     * Checks the result of an iscsiadm node-create command.
+     * Returns true if the node was created or already exists, false on 
failure.
+     */
+    boolean handleNodeCreateResult(String result, String volumeUuid) {
+        if (result == null) {
+            logger.debug("Successfully added iSCSI node for target {}", 
volumeUuid);
+            return true;
+        }
+        String msg = result.toLowerCase();
+        if (msg.contains("already exists") || msg.contains("database exists") 
|| msg.contains("exists")) {

Review Comment:
   As an improvement, non-blocking comment, would it be possible to check the 
exit status code instead of the returned message?



##########
plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java:
##########
@@ -65,14 +108,222 @@ public DataTO getTO(DataObject data) {
     @Override
     public DataStoreTO getStoreTO(DataStore store) { return null; }
 
+    @Override
+    public boolean volumesRequireGrantAccessWhenUsed() {
+        logger.info("OntapPrimaryDatastoreDriver: 
volumesRequireGrantAccessWhenUsed: Called");

Review Comment:
   Does it need to be at INFO level?



##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java:
##########
@@ -158,8 +159,58 @@ public boolean connectPhysicalDisk(String volumeUuid, 
KVMStoragePool pool, Map<S
         return true;
     }
 
+    /**
+     * Checks the result of an iscsiadm node-create command.
+     * Returns true if the node was created or already exists, false on 
failure.
+     */
+    boolean handleNodeCreateResult(String result, String volumeUuid) {
+        if (result == null) {
+            logger.debug("Successfully added iSCSI node for target {}", 
volumeUuid);
+            return true;
+        }
+        String msg = result.toLowerCase();
+        if (msg.contains("already exists") || msg.contains("database exists") 
|| msg.contains("exists")) {
+            logger.debug("iSCSI node already exists for target {}, 
proceeding", volumeUuid);
+            return true;
+        }
+        logger.debug("Failed to add iSCSI node for target {}: {}", volumeUuid, 
result);
+        return false;
+    }
+
+    /**
+     * Checks the result of an iscsiadm login command.
+     * Returns true if the login succeeded or session already exists, false on 
failure.
+     */
+    boolean handleLoginResult(String result, String volumeUuid) {
+        if (result == null) {
+            logger.debug("Successfully logged in to iSCSI target {}", 
volumeUuid);
+            return true;
+        }
+        String msg = result.toLowerCase();
+        if (msg.contains("already present") || msg.contains("already logged 
in") || msg.contains("session exists")) {

Review Comment:
   Similar here?



##########
plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java:
##########
@@ -98,14 +349,231 @@ public ChapInfo getChapInfo(DataObject dataObject) {
         return null;
     }
 
+    /**
+     * Grants a host access to a volume.
+     */
     @Override
     public boolean grantAccess(DataObject dataObject, Host host, DataStore 
dataStore) {
-       return false;
+        try {
+            if (dataStore == null) {
+                throw new InvalidParameterValueException("dataStore should not 
be null");
+            }
+            if (dataObject == null) {
+                throw new InvalidParameterValueException("dataObject should 
not be null");
+            }
+            if (host == null) {
+                throw new InvalidParameterValueException("host should not be 
null");
+            }

Review Comment:
   Similarly here



##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java:
##########
@@ -238,6 +289,15 @@ public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, 
KVMStoragePool pool) {
     }
 
     private long getDeviceSize(String deviceByPath) {
+        try {
+            if (!Files.exists(Paths.get(deviceByPath))) {
+                logger.debug("Device by-path does not exist yet: " + 
deviceByPath);
+                return 0L;
+            }
+        } catch (Exception ignore) {
+            // If FS check fails for any reason, fall back to blockdev call

Review Comment:
   I think this deserves a log line



##########
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java:
##########
@@ -158,8 +159,58 @@ public boolean connectPhysicalDisk(String volumeUuid, 
KVMStoragePool pool, Map<S
         return true;
     }
 
+    /**
+     * Checks the result of an iscsiadm node-create command.
+     * Returns true if the node was created or already exists, false on 
failure.
+     */
+    boolean handleNodeCreateResult(String result, String volumeUuid) {
+        if (result == null) {
+            logger.debug("Successfully added iSCSI node for target {}", 
volumeUuid);
+            return true;
+        }
+        String msg = result.toLowerCase();
+        if (msg.contains("already exists") || msg.contains("database exists") 
|| msg.contains("exists")) {
+            logger.debug("iSCSI node already exists for target {}, 
proceeding", volumeUuid);
+            return true;
+        }
+        logger.debug("Failed to add iSCSI node for target {}: {}", volumeUuid, 
result);
+        return false;
+    }
+
+    /**
+     * Checks the result of an iscsiadm login command.
+     * Returns true if the login succeeded or session already exists, false on 
failure.
+     */
+    boolean handleLoginResult(String result, String volumeUuid) {
+        if (result == null) {
+            logger.debug("Successfully logged in to iSCSI target {}", 
volumeUuid);
+            return true;
+        }
+        String msg = result.toLowerCase();
+        if (msg.contains("already present") || msg.contains("already logged 
in") || msg.contains("session exists")) {
+            logger.debug("iSCSI session already exists for target {}, 
proceeding", volumeUuid);
+            return true;
+        }
+        logger.debug("Failed to log in to iSCSI target {}: {}", volumeUuid, 
result);
+        return false;
+    }
+
+    private void rescanIscsiSessions(String iqn, String host, int port) {
+        Script rescanCmd = new Script(true, "iscsiadm", 0, logger);
+        rescanCmd.add("-m", "node");
+        rescanCmd.add("-T", iqn);
+        rescanCmd.add("-p", host + ":" + port);
+        rescanCmd.add("--rescan");
+        String rescanResult = rescanCmd.execute();
+        if (rescanResult != null) {
+            logger.warn("iSCSI session rescan returned: {}", rescanResult);
+        } else {
+            logger.debug("iSCSI session rescan completed successfully for 
{}@{}:{}", iqn, host, port);
+        }
+    }
+
     private void waitForDiskToBecomeAvailable(String volumeUuid, 
KVMStoragePool pool) {
-        int numberOfTries = 10;
+        int numberOfTries = 30;

Review Comment:
   Is it worth being configurable by administrators?



##########
plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java:
##########
@@ -65,14 +108,222 @@ public DataTO getTO(DataObject data) {
     @Override
     public DataStoreTO getStoreTO(DataStore store) { return null; }
 
+    @Override
+    public boolean volumesRequireGrantAccessWhenUsed() {
+        logger.info("OntapPrimaryDatastoreDriver: 
volumesRequireGrantAccessWhenUsed: Called");
+        return true;
+    }
+
+    /**
+     * Creates a volume on the ONTAP storage system.
+     */
     @Override
     public void createAsync(DataStore dataStore, DataObject dataObject, 
AsyncCompletionCallback<CreateCmdResult> callback) {
-        throw new UnsupportedOperationException("Create operation is not 
supported for ONTAP primary storage.");
+        CreateCmdResult createCmdResult = null;
+        String errMsg;
+
+        if (dataObject == null) {
+            throw new InvalidParameterValueException("dataObject should not be 
null");
+        }
+        if (dataStore == null) {
+            throw new InvalidParameterValueException("dataStore should not be 
null");
+        }
+        if (callback == null) {
+            throw new InvalidParameterValueException("callback should not be 
null");
+        }

Review Comment:
   What about something like this?
   ```suggestion
           if (ObjectUtils.anyNull(dataObject, dataStore, callback)) {
               throw new InvalidParameterValueException("dataObject, dataStore 
and callback must not be null");
           }
   ```



##########
plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/UnifiedSANStrategy.java:
##########
@@ -286,24 +370,226 @@ public AccessGroup getAccessGroup(Map<String, String> 
values) {
             AccessGroup accessGroup = new AccessGroup();
             accessGroup.setIgroup(igroup);
             return accessGroup;
-        } catch (Exception e) {
-            String errMsg = e.getMessage();
-            if (errMsg != null && errMsg.contains("not found")) {
-                logger.warn("getAccessGroup: Igroup '{}' not found on SVM '{}' 
({}). Returning null.", igroupName, svmName, errMsg);
+        } catch (FeignException e) {
+            if (e.status() == 404) {
+                logger.warn("getAccessGroup: Igroup '{}' not found on SVM '{}' 
(status 404). Returning null.", igroupName, svmName);
                 return null;
             }
-            logger.error("Exception occurred while fetching Igroup, Exception: 
{}", errMsg);
-            throw new CloudRuntimeException("Failed to fetch Igroup details: " 
+ errMsg);
+            logger.error("FeignException occurred while fetching Igroup, 
Status: {}, Exception: {}", e.status(), e.getMessage());
+            throw new CloudRuntimeException("Failed to fetch Igroup details: " 
+ e.getMessage());
+        } catch (Exception e) {
+            logger.error("Exception occurred while fetching Igroup, Exception: 
{}", e.getMessage());
+            throw new CloudRuntimeException("Failed to fetch Igroup details: " 
+ e.getMessage());
         }
     }
 
-    public Map<String, String> enableLogicalAccess(Map<String, String> values) 
{
-        return null;
+    public String enableLogicalAccess(Map<String, String> values) {
+        logger.info("enableLogicalAccess : Create LunMap");

Review Comment:
   Are both consecutive log lines needed? Similar pattern is also present in 
other methods as well



##########
plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java:
##########
@@ -98,14 +349,231 @@ public ChapInfo getChapInfo(DataObject dataObject) {
         return null;
     }
 
+    /**
+     * Grants a host access to a volume.
+     */
     @Override
     public boolean grantAccess(DataObject dataObject, Host host, DataStore 
dataStore) {
-       return false;
+        try {
+            if (dataStore == null) {
+                throw new InvalidParameterValueException("dataStore should not 
be null");
+            }
+            if (dataObject == null) {
+                throw new InvalidParameterValueException("dataObject should 
not be null");
+            }
+            if (host == null) {
+                throw new InvalidParameterValueException("host should not be 
null");
+            }
+
+            StoragePoolVO storagePool = 
storagePoolDao.findById(dataStore.getId());
+            if (storagePool == null) {
+                logger.error("grantAccess: Storage Pool not found for id: " + 
dataStore.getId());
+                throw new CloudRuntimeException("Storage Pool not found for 
id: " + dataStore.getId());
+            }
+
+            // ONTAP managed storage only supports cluster and zone scoped 
pools
+            if (storagePool.getScope() != ScopeType.CLUSTER && 
storagePool.getScope() != ScopeType.ZONE) {
+                logger.error("grantAccess: Only Cluster and Zone scoped 
primary storage is supported for storage Pool: " + storagePool.getName());
+                throw new CloudRuntimeException("Only Cluster and Zone scoped 
primary storage is supported for Storage Pool: " + storagePool.getName());
+            }
+
+            if (dataObject.getType() == DataObjectType.VOLUME) {
+                VolumeVO volumeVO = volumeDao.findById(dataObject.getId());
+                if (volumeVO == null) {
+                    logger.error("grantAccess: CloudStack Volume not found for 
id: " + dataObject.getId());
+                    throw new CloudRuntimeException("CloudStack Volume not 
found for id: " + dataObject.getId());
+                }
+
+                Map<String, String> details = 
storagePoolDetailsDao.listDetailsKeyPairs(storagePool.getId());
+                String svmName = details.get(OntapStorageConstants.SVM_NAME);
+
+                if 
(ProtocolType.ISCSI.name().equalsIgnoreCase(details.get(OntapStorageConstants.PROTOCOL)))
 {
+                    // Only retrieve LUN name for iSCSI volumes
+                    String cloudStackVolumeName = 
volumeDetailsDao.findDetail(volumeVO.getId(), 
OntapStorageConstants.LUN_DOT_NAME).getValue();
+                    UnifiedSANStrategy sanStrategy = (UnifiedSANStrategy) 
OntapStorageUtils.getStrategyByStoragePoolDetails(details);
+                    String accessGroupName = 
OntapStorageUtils.getIgroupName(svmName, host.getName());
+
+                    // Validate if Igroup exist ONTAP for this host as we may 
be using delete_on_unmap= true and igroup may be deleted by ONTAP automatically
+                    Map<String, String> getAccessGroupMap = Map.of(
+                            OntapStorageConstants.NAME, accessGroupName,
+                            OntapStorageConstants.SVM_DOT_NAME, svmName
+                    );
+                    AccessGroup accessGroup = 
sanStrategy.getAccessGroup(getAccessGroupMap);
+                    if(accessGroup == null || accessGroup.getIgroup() == null) 
{
+                        logger.info("grantAccess: Igroup {} does not exist for 
the host {} : Need to create Igroup for the host ", accessGroupName, 
host.getName());
+                        // create the igroup for the host and perform 
lun-mapping
+                        accessGroup = new AccessGroup();
+                        List<HostVO> hosts = new ArrayList<>();
+                        hosts.add((HostVO) host);
+                        accessGroup.setHostsToConnect(hosts);
+                        accessGroup.setStoragePoolId(storagePool.getId());
+                        accessGroup = 
sanStrategy.createAccessGroup(accessGroup);
+                    }else{
+                        logger.info("grantAccess: Igroup {} already exist for 
the host {}: ", accessGroup.getIgroup().getName() ,host.getName());
+                        /* TODO Below cases will be covered later, for now 
they will be a pre-requisite on customer side
+                          1. Igroup exist with the same name but host 
initiator has been rempved
+                          2.  Igroup exist with the same name but host 
initiator has been changed may be due to new NIC or new adapter
+                          In both cases we need to verify current host 
initiator is registered in the igroup before allowing access
+                          Incase it is not , add it and proceed for lun-mapping
+                         */
+                    }
+                    logger.info("grantAccess: Igroup {}  is present now with 
initiators {} ", accessGroup.getIgroup().getName(), 
accessGroup.getIgroup().getInitiators());
+                    // Create or retrieve existing LUN mapping
+                    String lunNumber = sanStrategy.ensureLunMapped(svmName, 
cloudStackVolumeName, accessGroupName);
+
+                    // Update volume path if changed (e.g., after migration or 
re-mapping)
+                    String iscsiPath = OntapStorageConstants.SLASH + 
storagePool.getPath() + OntapStorageConstants.SLASH + lunNumber;
+                    if (volumeVO.getPath() == null || 
!volumeVO.getPath().equals(iscsiPath)) {
+                        volumeVO.set_iScsiName(iscsiPath);
+                        volumeVO.setPath(iscsiPath);
+                    }

Review Comment:
   Can these lines be extracted to a method?



##########
plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java:
##########
@@ -98,14 +349,231 @@ public ChapInfo getChapInfo(DataObject dataObject) {
         return null;
     }
 
+    /**
+     * Grants a host access to a volume.
+     */
     @Override
     public boolean grantAccess(DataObject dataObject, Host host, DataStore 
dataStore) {
-       return false;
+        try {
+            if (dataStore == null) {
+                throw new InvalidParameterValueException("dataStore should not 
be null");
+            }
+            if (dataObject == null) {
+                throw new InvalidParameterValueException("dataObject should 
not be null");
+            }
+            if (host == null) {
+                throw new InvalidParameterValueException("host should not be 
null");
+            }
+
+            StoragePoolVO storagePool = 
storagePoolDao.findById(dataStore.getId());
+            if (storagePool == null) {
+                logger.error("grantAccess: Storage Pool not found for id: " + 
dataStore.getId());
+                throw new CloudRuntimeException("Storage Pool not found for 
id: " + dataStore.getId());
+            }
+
+            // ONTAP managed storage only supports cluster and zone scoped 
pools
+            if (storagePool.getScope() != ScopeType.CLUSTER && 
storagePool.getScope() != ScopeType.ZONE) {
+                logger.error("grantAccess: Only Cluster and Zone scoped 
primary storage is supported for storage Pool: " + storagePool.getName());
+                throw new CloudRuntimeException("Only Cluster and Zone scoped 
primary storage is supported for Storage Pool: " + storagePool.getName());
+            }
+
+            if (dataObject.getType() == DataObjectType.VOLUME) {
+                VolumeVO volumeVO = volumeDao.findById(dataObject.getId());
+                if (volumeVO == null) {
+                    logger.error("grantAccess: CloudStack Volume not found for 
id: " + dataObject.getId());
+                    throw new CloudRuntimeException("CloudStack Volume not 
found for id: " + dataObject.getId());
+                }
+
+                Map<String, String> details = 
storagePoolDetailsDao.listDetailsKeyPairs(storagePool.getId());
+                String svmName = details.get(OntapStorageConstants.SVM_NAME);
+
+                if 
(ProtocolType.ISCSI.name().equalsIgnoreCase(details.get(OntapStorageConstants.PROTOCOL)))
 {
+                    // Only retrieve LUN name for iSCSI volumes
+                    String cloudStackVolumeName = 
volumeDetailsDao.findDetail(volumeVO.getId(), 
OntapStorageConstants.LUN_DOT_NAME).getValue();
+                    UnifiedSANStrategy sanStrategy = (UnifiedSANStrategy) 
OntapStorageUtils.getStrategyByStoragePoolDetails(details);
+                    String accessGroupName = 
OntapStorageUtils.getIgroupName(svmName, host.getName());
+
+                    // Validate if Igroup exist ONTAP for this host as we may 
be using delete_on_unmap= true and igroup may be deleted by ONTAP automatically
+                    Map<String, String> getAccessGroupMap = Map.of(
+                            OntapStorageConstants.NAME, accessGroupName,
+                            OntapStorageConstants.SVM_DOT_NAME, svmName
+                    );
+                    AccessGroup accessGroup = 
sanStrategy.getAccessGroup(getAccessGroupMap);
+                    if(accessGroup == null || accessGroup.getIgroup() == null) 
{
+                        logger.info("grantAccess: Igroup {} does not exist for 
the host {} : Need to create Igroup for the host ", accessGroupName, 
host.getName());
+                        // create the igroup for the host and perform 
lun-mapping
+                        accessGroup = new AccessGroup();
+                        List<HostVO> hosts = new ArrayList<>();
+                        hosts.add((HostVO) host);
+                        accessGroup.setHostsToConnect(hosts);
+                        accessGroup.setStoragePoolId(storagePool.getId());
+                        accessGroup = 
sanStrategy.createAccessGroup(accessGroup);
+                    }else{
+                        logger.info("grantAccess: Igroup {} already exist for 
the host {}: ", accessGroup.getIgroup().getName() ,host.getName());
+                        /* TODO Below cases will be covered later, for now 
they will be a pre-requisite on customer side
+                          1. Igroup exist with the same name but host 
initiator has been rempved
+                          2.  Igroup exist with the same name but host 
initiator has been changed may be due to new NIC or new adapter
+                          In both cases we need to verify current host 
initiator is registered in the igroup before allowing access
+                          Incase it is not , add it and proceed for lun-mapping
+                         */
+                    }
+                    logger.info("grantAccess: Igroup {}  is present now with 
initiators {} ", accessGroup.getIgroup().getName(), 
accessGroup.getIgroup().getInitiators());
+                    // Create or retrieve existing LUN mapping
+                    String lunNumber = sanStrategy.ensureLunMapped(svmName, 
cloudStackVolumeName, accessGroupName);
+
+                    // Update volume path if changed (e.g., after migration or 
re-mapping)
+                    String iscsiPath = OntapStorageConstants.SLASH + 
storagePool.getPath() + OntapStorageConstants.SLASH + lunNumber;
+                    if (volumeVO.getPath() == null || 
!volumeVO.getPath().equals(iscsiPath)) {
+                        volumeVO.set_iScsiName(iscsiPath);
+                        volumeVO.setPath(iscsiPath);
+                    }
+                } else if 
(ProtocolType.NFS3.name().equalsIgnoreCase(details.get(OntapStorageConstants.PROTOCOL)))
 {
+                    // For NFS, no access grant needed - file is accessible 
via mount
+                    logger.debug("grantAccess: NFS volume [{}], no igroup 
mapping required", volumeVO.getUuid());
+                    return true;
+                }
+                volumeVO.setPoolType(storagePool.getPoolType());
+                volumeVO.setPoolId(storagePool.getId());
+                volumeDao.update(volumeVO.getId(), volumeVO);
+            } else {
+                logger.error("Invalid DataObjectType (" + dataObject.getType() 
+ ") passed to grantAccess");
+                throw new CloudRuntimeException("Invalid DataObjectType (" + 
dataObject.getType() + ") passed to grantAccess");
+            }
+            return true;
+        } catch (Exception e) {
+            logger.error("grantAccess: Failed for dataObject [{}]: {}", 
dataObject, e.getMessage());
+            throw new CloudRuntimeException("Failed with error: " + 
e.getMessage(), e);
+        }
     }
 
+    /**
+     * Revokes a host's access to a volume.
+     */
     @Override
     public void revokeAccess(DataObject dataObject, Host host, DataStore 
dataStore) {
-        throw new UnsupportedOperationException("Revoke access operation is 
not supported for ONTAP primary storage.");
+        try {
+            if (dataStore == null) {
+                throw new InvalidParameterValueException("dataStore should not 
be null");
+            }
+            if (dataObject == null) {
+                throw new InvalidParameterValueException("dataObject should 
not be null");
+            }
+            if (host == null) {
+                throw new InvalidParameterValueException("host should not be 
null");
+            }
+
+            StoragePoolVO storagePool = 
storagePoolDao.findById(dataStore.getId());
+            if (storagePool == null) {
+                logger.error("revokeAccess: Storage Pool not found for id: " + 
dataStore.getId());
+                throw new CloudRuntimeException("Storage Pool not found for 
id: " + dataStore.getId());
+            }
+
+            if (storagePool.getScope() != ScopeType.CLUSTER && 
storagePool.getScope() != ScopeType.ZONE) {
+                logger.error("revokeAccess: Only Cluster and Zone scoped 
primary storage is supported for storage Pool: " + storagePool.getName());
+                throw new CloudRuntimeException("Only Cluster and Zone scoped 
primary storage is supported for Storage Pool: " + storagePool.getName());
+            }
+
+            if (dataObject.getType() == DataObjectType.VOLUME) {
+                VolumeVO volumeVO = volumeDao.findById(dataObject.getId());
+                if (volumeVO == null) {
+                    logger.error("revokeAccess: CloudStack Volume not found 
for id: " + dataObject.getId());
+                    throw new CloudRuntimeException("CloudStack Volume not 
found for id: " + dataObject.getId());
+                }
+                revokeAccessForVolume(storagePool, volumeVO, host);
+            } else {
+                logger.error("revokeAccess: Invalid DataObjectType (" + 
dataObject.getType() + ") passed to revokeAccess");
+                throw new CloudRuntimeException("Invalid DataObjectType (" + 
dataObject.getType() + ") passed to revokeAccess");
+            }
+        } catch (Exception e) {
+            logger.error("revokeAccess: Failed for dataObject [{}]: {}", 
dataObject, e.getMessage());
+            throw new CloudRuntimeException("Failed with error: " + 
e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Revokes volume access for the specified host.
+     */
+    private void revokeAccessForVolume(StoragePoolVO storagePool, VolumeVO 
volumeVO, Host host) {
+        logger.info("revokeAccessForVolume: Revoking access to volume [{}] for 
host [{}]", volumeVO.getName(), host.getName());
+
+        Map<String, String> details = 
storagePoolDetailsDao.listDetailsKeyPairs(storagePool.getId());
+        StorageStrategy storageStrategy = 
OntapStorageUtils.getStrategyByStoragePoolDetails(details);
+        String svmName = details.get(OntapStorageConstants.SVM_NAME);
+
+        if 
(ProtocolType.ISCSI.name().equalsIgnoreCase(details.get(OntapStorageConstants.PROTOCOL)))
 {
+            String accessGroupName = OntapStorageUtils.getIgroupName(svmName, 
host.getName());
+
+            // Retrieve LUN name from volume details; if missing, volume may 
not have been fully created
+            VolumeDetailVO lunDetail = 
volumeDetailsDao.findDetail(volumeVO.getId(), 
OntapStorageConstants.LUN_DOT_NAME);
+            String lunName = lunDetail != null ? lunDetail.getValue() : null;
+            if (lunName == null) {
+                logger.warn("revokeAccessForVolume: No LUN name found for 
volume [{}]; skipping revoke", volumeVO.getId());
+                return;
+            }
+
+            // Verify LUN still exists on ONTAP (may have been manually 
deleted)
+            CloudStackVolume cloudStackVolume = 
getCloudStackVolumeByName(storageStrategy, svmName, lunName);
+            if (cloudStackVolume == null || cloudStackVolume.getLun() == null 
|| cloudStackVolume.getLun().getUuid() == null) {
+                logger.warn("revokeAccessForVolume: LUN for volume [{}] not 
found on ONTAP, skipping revoke", volumeVO.getId());
+                return;
+            }
+
+            // Verify igroup still exists on ONTAP
+            AccessGroup accessGroup = getAccessGroupByName(storageStrategy, 
svmName, accessGroupName);
+            if (accessGroup == null || accessGroup.getIgroup() == null || 
accessGroup.getIgroup().getUuid() == null) {
+                logger.warn("revokeAccessForVolume: iGroup [{}] not found on 
ONTAP, skipping revoke", accessGroupName);
+                return;
+            }
+
+            // Verify host initiator is in the igroup before attempting to 
remove mapping
+            SANStrategy sanStrategy = (UnifiedSANStrategy) storageStrategy;
+            if 
(!sanStrategy.validateInitiatorInAccessGroup(host.getStorageUrl(), svmName, 
accessGroup.getIgroup())) {
+                logger.warn("revokeAccessForVolume: Initiator [{}] is not in 
iGroup [{}], skipping revoke",
+                        host.getStorageUrl(), accessGroupName);
+                return;
+            }
+
+            // Remove the LUN mapping from the igroup
+            Map<String, String> disableLogicalAccessMap = new HashMap<>();
+            disableLogicalAccessMap.put(OntapStorageConstants.LUN_DOT_UUID, 
cloudStackVolume.getLun().getUuid());
+            disableLogicalAccessMap.put(OntapStorageConstants.IGROUP_DOT_UUID, 
accessGroup.getIgroup().getUuid());
+            storageStrategy.disableLogicalAccess(disableLogicalAccessMap);
+
+            logger.info("revokeAccessForVolume: Successfully revoked access to 
LUN [{}] for host [{}]",
+                    lunName, host.getName());

Review Comment:
   Similarly here



-- 
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