This is an automated email from the ASF dual-hosted git repository.

yasith pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata.git


The following commit(s) were added to refs/heads/master by this push:
     new cf9ccf1858 Add API to retrieve storage volume information from 
compute/storage resources (#571)
cf9ccf1858 is described below

commit cf9ccf1858d25bff5f4e07ba502840ac93406928
Author: Lahiru Jayathilake <[email protected]>
AuthorDate: Mon Nov 10 20:39:49 2025 -0500

    Add API to retrieve storage volume information from compute/storage 
resources (#571)
    
    * Add getResourceStorageInfo API to retrieve disk usage from 
compute/storage resources
    * spotless apply
    * handle the resource not found exceptions and added a helper method to 
check the gateway profile existence
    * generated the sdk code
    * bump sdk to 2.2.5
    
    ---------
    
    Co-authored-by: yasithdev <[email protected]>
---
 .../apache/airavata/agents/api/AgentAdaptor.java   |  30 +-
 .../api/server/handler/AiravataServerHandler.java  | 344 +++++++++++++++++++++
 .../airavata/helix/adaptor/SSHJAgentAdaptor.java   | 131 ++++++++
 .../airavata/helix/adaptor/SSHJStorageAdaptor.java |   6 +
 .../airavata/helix/agent/ssh/SshAgentAdaptor.java  |   7 +
 .../core/support/adaptor/AdaptorSupportImpl.java   |  97 +++++-
 .../helix/core/support/adaptor/AgentStore.java     |  38 +++
 .../helix/task/api/support/AdaptorSupport.java     |  18 +-
 .../airavata/api/Airavata-remote                   |   7 +
 .../airavata-python-sdk/airavata/api/Airavata.py   | 309 ++++++++++++++++++
 .../model/appcatalog/storageresource/ttypes.py     | 190 ++++++++++++
 .../airavata_sdk/clients/api_server_client.py      |   1 +
 dev-tools/airavata-python-sdk/pyproject.toml       |   2 +-
 .../airavata-apis/airavata_api.thrift              |  23 +-
 .../data-models/storage_resource_model.thrift      |  27 ++
 .../generate-thrift-stubs.sh                       |   3 +-
 16 files changed, 1209 insertions(+), 24 deletions(-)

diff --git 
a/airavata-api/src/main/java/org/apache/airavata/agents/api/AgentAdaptor.java 
b/airavata-api/src/main/java/org/apache/airavata/agents/api/AgentAdaptor.java
index 38375c714f..245cad8c69 100644
--- 
a/airavata-api/src/main/java/org/apache/airavata/agents/api/AgentAdaptor.java
+++ 
b/airavata-api/src/main/java/org/apache/airavata/agents/api/AgentAdaptor.java
@@ -22,6 +22,7 @@ package org.apache.airavata.agents.api;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.List;
+import org.apache.airavata.model.appcatalog.storageresource.StorageVolumeInfo;
 
 /**
  * TODO: Class level comments please
@@ -31,30 +32,31 @@ import java.util.List;
  */
 public interface AgentAdaptor {
 
-    public void init(String computeResource, String gatewayId, String userId, 
String token) throws AgentException;
+    void init(String computeResource, String gatewayId, String userId, String 
token) throws AgentException;
 
-    public void destroy();
+    void destroy();
 
-    public CommandOutput executeCommand(String command, String 
workingDirectory) throws AgentException;
+    CommandOutput executeCommand(String command, String workingDirectory) 
throws AgentException;
 
-    public void createDirectory(String path) throws AgentException;
+    void createDirectory(String path) throws AgentException;
 
-    public void createDirectory(String path, boolean recursive) throws 
AgentException;
+    void createDirectory(String path, boolean recursive) throws AgentException;
 
-    public void uploadFile(String localFile, String remoteFile) throws 
AgentException;
+    void uploadFile(String localFile, String remoteFile) throws AgentException;
 
-    public void uploadFile(InputStream localInStream, FileMetadata metadata, 
String remoteFile) throws AgentException;
+    void uploadFile(InputStream localInStream, FileMetadata metadata, String 
remoteFile) throws AgentException;
 
-    public void downloadFile(String remoteFile, String localFile) throws 
AgentException;
+    void downloadFile(String remoteFile, String localFile) throws 
AgentException;
 
-    public void downloadFile(String remoteFile, OutputStream localOutStream, 
FileMetadata metadata)
-            throws AgentException;
+    void downloadFile(String remoteFile, OutputStream localOutStream, 
FileMetadata metadata) throws AgentException;
 
-    public List<String> listDirectory(String path) throws AgentException;
+    List<String> listDirectory(String path) throws AgentException;
 
-    public Boolean doesFileExist(String filePath) throws AgentException;
+    Boolean doesFileExist(String filePath) throws AgentException;
 
-    public List<String> getFileNameFromExtension(String fileName, String 
parentPath) throws AgentException;
+    List<String> getFileNameFromExtension(String fileName, String parentPath) 
throws AgentException;
 
-    public FileMetadata getFileMetadata(String remoteFile) throws 
AgentException;
+    FileMetadata getFileMetadata(String remoteFile) throws AgentException;
+
+    StorageVolumeInfo getStorageVolumeInfo(String location) throws 
AgentException;
 }
diff --git 
a/airavata-api/src/main/java/org/apache/airavata/api/server/handler/AiravataServerHandler.java
 
b/airavata-api/src/main/java/org/apache/airavata/api/server/handler/AiravataServerHandler.java
index 4be740eebd..c8a89566b3 100644
--- 
a/airavata-api/src/main/java/org/apache/airavata/api/server/handler/AiravataServerHandler.java
+++ 
b/airavata-api/src/main/java/org/apache/airavata/api/server/handler/AiravataServerHandler.java
@@ -26,6 +26,8 @@ import org.apache.airavata.accountprovisioning.ConfigParam;
 import org.apache.airavata.accountprovisioning.SSHAccountManager;
 import org.apache.airavata.accountprovisioning.SSHAccountProvisionerFactory;
 import org.apache.airavata.accountprovisioning.SSHAccountProvisionerProvider;
+import org.apache.airavata.agents.api.AgentAdaptor;
+import org.apache.airavata.agents.api.AgentException;
 import org.apache.airavata.api.Airavata;
 import org.apache.airavata.api.airavata_apiConstants;
 import org.apache.airavata.common.exception.AiravataException;
@@ -35,6 +37,7 @@ import org.apache.airavata.common.utils.Constants;
 import org.apache.airavata.common.utils.ServerSettings;
 import org.apache.airavata.common.utils.ThriftClientPool;
 import org.apache.airavata.credential.store.cpi.CredentialStoreService;
+import org.apache.airavata.helix.core.support.adaptor.AdaptorSupportImpl;
 import org.apache.airavata.messaging.core.MessageContext;
 import org.apache.airavata.messaging.core.MessagingFactory;
 import org.apache.airavata.messaging.core.Publisher;
@@ -57,6 +60,7 @@ import 
org.apache.airavata.model.appcatalog.groupresourceprofile.GroupResourcePr
 import org.apache.airavata.model.appcatalog.parser.Parser;
 import org.apache.airavata.model.appcatalog.parser.ParsingTemplate;
 import 
org.apache.airavata.model.appcatalog.storageresource.StorageResourceDescription;
+import org.apache.airavata.model.appcatalog.storageresource.StorageVolumeInfo;
 import 
org.apache.airavata.model.appcatalog.userresourceprofile.UserComputeResourcePreference;
 import 
org.apache.airavata.model.appcatalog.userresourceprofile.UserResourceProfile;
 import 
org.apache.airavata.model.appcatalog.userresourceprofile.UserStoragePreference;
@@ -98,6 +102,7 @@ import 
org.apache.airavata.service.security.interceptor.SecurityCheck;
 import org.apache.airavata.sharing.registry.models.*;
 import org.apache.airavata.sharing.registry.service.cpi.SharingRegistryService;
 import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.apache.thrift.TApplicationException;
 import org.apache.thrift.TException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -3839,6 +3844,81 @@ public class AiravataServerHandler implements 
Airavata.Iface {
         }
     }
 
+    @Override
+    @SecurityCheck
+    public StorageVolumeInfo getResourceStorageInfo(AuthzToken authzToken, 
String resourceId, String location)
+            throws TException {
+        String gatewayId = authzToken.getClaimsMap().get(Constants.GATEWAY_ID);
+        String userId = authzToken.getClaimsMap().get(Constants.USER_NAME);
+        RegistryService.Client regClient = registryClientPool.getResource();
+        StorageInfoContext context;
+
+        try {
+            Optional<ComputeResourceDescription> computeResourceOp = 
Optional.empty();
+            try {
+                ComputeResourceDescription computeResource = 
regClient.getComputeResource(resourceId);
+                if (computeResource != null) {
+                    computeResourceOp = Optional.of(computeResource);
+                }
+            } catch (TApplicationException e) {
+                // TApplicationException with "unknown result" means resource 
not found (null return for non-nullable
+                // type)
+                logger.debug("Compute resource {} not found 
(TApplicationException): {}", resourceId, e.getMessage());
+            }
+
+            Optional<StorageResourceDescription> storageResourceOp = 
Optional.empty();
+            if (computeResourceOp.isEmpty()) {
+                try {
+                    StorageResourceDescription storageResource = 
regClient.getStorageResource(resourceId);
+                    if (storageResource != null) {
+                        storageResourceOp = Optional.of(storageResource);
+                    }
+                } catch (TApplicationException e) {
+                    // TApplicationException with "unknown result" means 
resource not found (null return for
+                    // non-nullable type)
+                    logger.debug(
+                            "Storage resource {} not found 
(TApplicationException): {}", resourceId, e.getMessage());
+                }
+            }
+
+            if (computeResourceOp.isEmpty() && storageResourceOp.isEmpty()) {
+                logger.error(
+                        "Resource with ID {} not found as either compute 
resource or storage resource", resourceId);
+                throw new InvalidRequestException("Resource with ID '" + 
resourceId
+                        + "' not found as either compute resource or storage 
resource");
+            }
+
+            if (computeResourceOp.isPresent()) {
+                logger.debug("Found compute resource with ID {}. Resolving 
login username and credentials", resourceId);
+                context = resolveComputeStorageInfoContext(authzToken, 
gatewayId, userId, resourceId);
+            } else {
+                logger.debug("Found storage resource with ID {}. Resolving 
login username and credentials", resourceId);
+                context = resolveStorageStorageInfoContext(authzToken, 
gatewayId, userId, resourceId);
+            }
+
+            registryClientPool.returnResource(regClient);
+            regClient = null;
+
+            return context.adaptor.getStorageVolumeInfo(location);
+
+        } catch (InvalidRequestException | AiravataClientException e) {
+            if (regClient != null) {
+                registryClientPool.returnResource(regClient);
+            }
+            logger.error("Error while retrieving storage resource.", e);
+            throw e;
+
+        } catch (Exception e) {
+            logger.error("Error while retrieving storage volume info for 
resource {}", resourceId, e);
+            registryClientPool.returnBrokenResource(regClient);
+
+            AiravataSystemException exception = new AiravataSystemException();
+            exception.setAiravataErrorType(AiravataErrorType.INTERNAL_ERROR);
+            exception.setMessage("Error while retrieving storage volume info. 
More info: " + e.getMessage());
+            throw exception;
+        }
+    }
+
     /**
      * Add a Local Job Submission details to a compute resource
      * App catalog will return a jobSubmissionInterfaceId which will be added 
to the jobSubmissionInterfaces.
@@ -7232,4 +7312,268 @@ public class AiravataServerHandler implements 
Airavata.Iface {
             return GatewayGroupsInitializer.initializeGatewayGroups(gatewayId);
         }
     }
+
+    /**
+     * To hold storage info context (login username, credential token, and 
adaptor)
+     */
+    private record StorageInfoContext(String loginUserName, String 
credentialToken, AgentAdaptor adaptor) {}
+
+    /**
+     * Check if a gateway resource profile exists
+     */
+    private boolean isGatewayResourceProfileExists(String gatewayId) throws 
TException {
+        RegistryService.Client regClient = registryClientPool.getResource();
+        try {
+            try {
+                GatewayResourceProfile profile = 
regClient.getGatewayResourceProfile(gatewayId);
+                registryClientPool.returnResource(regClient);
+                return profile != null;
+            } catch (org.apache.thrift.TApplicationException e) {
+                logger.error("Gateway resource profile does not exist for 
gateway: {}", gatewayId, e);
+                registryClientPool.returnResource(regClient);
+                return false;
+            }
+        } catch (Exception e) {
+            registryClientPool.returnBrokenResource(regClient);
+            logger.error("Error while checking if gateway resource profile 
exists", e);
+            throw e;
+        }
+    }
+
+    private AiravataClientException clientException(AiravataErrorType 
errorType, String parameter) {
+        AiravataClientException exception = new AiravataClientException();
+        exception.setAiravataErrorType(errorType);
+        exception.setParameter(parameter);
+        return exception;
+    }
+
+    /**
+     * Resolves compute resource storage info context (login username, 
credential token, and adaptor).
+     * Handles user preference → group preference fallback for both login and 
credentials.
+     */
+    private StorageInfoContext resolveComputeStorageInfoContext(
+            AuthzToken authzToken, String gatewayId, String userId, String 
resourceId)
+            throws AgentException, TException {
+        String loginUserName = null;
+        boolean loginFromUserPref = false;
+        GroupComputeResourcePreference groupComputePref = null;
+        GroupResourceProfile groupResourceProfile = null;
+
+        UserComputeResourcePreference userComputePref = null;
+        if (isUserResourceProfileExists(authzToken, userId, gatewayId)) {
+            userComputePref = getUserComputeResourcePreference(authzToken, 
userId, gatewayId, resourceId);
+        } else {
+            logger.debug(
+                    "User resource profile does not exist for user {} in 
gateway {}, will try group preferences",
+                    userId,
+                    gatewayId);
+        }
+
+        if (userComputePref != null
+                && userComputePref.getLoginUserName() != null
+                && !userComputePref.getLoginUserName().trim().isEmpty()) {
+            loginUserName = userComputePref.getLoginUserName();
+            loginFromUserPref = true;
+            logger.debug("Using user preference login username: {}", 
loginUserName);
+
+        } else {
+            // Fallback to GroupComputeResourcePreference
+            List<GroupResourceProfile> groupResourceProfiles = 
getGroupResourceList(authzToken, gatewayId);
+            for (GroupResourceProfile groupProfile : groupResourceProfiles) {
+                List<GroupComputeResourcePreference> groupComputePrefs = 
groupProfile.getComputePreferences();
+
+                if (groupComputePrefs != null && !groupComputePrefs.isEmpty()) 
{
+                    for (GroupComputeResourcePreference groupPref : 
groupComputePrefs) {
+                        if (resourceId.equals(groupPref.getComputeResourceId())
+                                && groupPref.getLoginUserName() != null
+                                && 
!groupPref.getLoginUserName().trim().isEmpty()) {
+                            loginUserName = groupPref.getLoginUserName();
+                            groupComputePref = groupPref;
+                            groupResourceProfile = groupProfile;
+                            logger.debug(
+                                    "Using login username from group compute 
resource preference for resource {}",
+                                    resourceId);
+                            break;
+                        }
+                    }
+                }
+                if (loginUserName != null) {
+                    break;
+                }
+            }
+            if (loginUserName == null) {
+                logger.debug("No login username found for compute resource 
{}", resourceId);
+                throw new InvalidRequestException("No login username found for 
compute resource " + resourceId);
+            }
+        }
+
+        // Resolve credential token based on where login came from
+        String credentialToken;
+        if (loginFromUserPref) {
+            // Login username came from user preference. Use user preference 
token → user profile token
+            if (userComputePref != null
+                    && 
userComputePref.getResourceSpecificCredentialStoreToken() != null
+                    && !userComputePref
+                            .getResourceSpecificCredentialStoreToken()
+                            .trim()
+                            .isEmpty()) {
+                credentialToken = 
userComputePref.getResourceSpecificCredentialStoreToken();
+            } else {
+                UserResourceProfile userResourceProfile = 
getUserResourceProfile(authzToken, userId, gatewayId);
+                if (userResourceProfile == null
+                        || userResourceProfile.getCredentialStoreToken() == 
null
+                        || 
userResourceProfile.getCredentialStoreToken().trim().isEmpty()) {
+                    logger.error("No credential store token found for user {} 
in gateway {}", userId, gatewayId);
+                    throw clientException(
+                            AiravataErrorType.AUTHENTICATION_FAILURE,
+                            "No credential store token found for user " + 
userId + " in gateway " + gatewayId);
+                }
+                credentialToken = 
userResourceProfile.getCredentialStoreToken();
+            }
+        } else {
+            // Login username came from group preference. Use group preference 
token → group profile default token →
+            // user profile token (fallback)
+            if (groupComputePref != null
+                    && 
groupComputePref.getResourceSpecificCredentialStoreToken() != null
+                    && !groupComputePref
+                            .getResourceSpecificCredentialStoreToken()
+                            .trim()
+                            .isEmpty()) {
+                credentialToken = 
groupComputePref.getResourceSpecificCredentialStoreToken();
+
+            } else if (groupResourceProfile != null
+                    && groupResourceProfile.getDefaultCredentialStoreToken() 
!= null
+                    && !groupResourceProfile
+                            .getDefaultCredentialStoreToken()
+                            .trim()
+                            .isEmpty()) {
+                credentialToken = 
groupResourceProfile.getDefaultCredentialStoreToken();
+
+            } else {
+                UserResourceProfile userResourceProfile = 
getUserResourceProfile(authzToken, userId, gatewayId);
+                if (userResourceProfile == null
+                        || userResourceProfile.getCredentialStoreToken() == 
null
+                        || 
userResourceProfile.getCredentialStoreToken().trim().isEmpty()) {
+                    logger.error("No credential store token found for user {} 
in gateway {}", userId, gatewayId);
+                    throw clientException(
+                            AiravataErrorType.AUTHENTICATION_FAILURE,
+                            "No credential store token found for compute 
resource " + resourceId);
+                }
+                credentialToken = 
userResourceProfile.getCredentialStoreToken();
+            }
+        }
+
+        AgentAdaptor adaptor = AdaptorSupportImpl.getInstance()
+                .fetchComputeSSHAdaptor(gatewayId, resourceId, 
credentialToken, userId, loginUserName);
+        logger.info("Resolved resource {} as compute resource to fetch storage 
details", resourceId);
+
+        return new StorageInfoContext(loginUserName, credentialToken, adaptor);
+    }
+
+    /**
+     * Resolves storage resource storage info context (login username, 
credential token, and adaptor).
+     * Handles user preference → gateway preference fallback for both login 
and credentials.
+     */
+    private StorageInfoContext resolveStorageStorageInfoContext(
+            AuthzToken authzToken, String gatewayId, String userId, String 
resourceId)
+            throws AgentException, TException {
+        UserStoragePreference userStoragePref = null;
+        if (isUserResourceProfileExists(authzToken, userId, gatewayId)) {
+            userStoragePref = getUserStoragePreference(authzToken, userId, 
gatewayId, resourceId);
+        } else {
+            logger.debug(
+                    "User resource profile does not exist for user {} in 
gateway {}, will try gateway preferences",
+                    userId,
+                    gatewayId);
+        }
+
+        StoragePreference storagePref = null;
+        if (isGatewayResourceProfileExists(gatewayId)) {
+            storagePref = getGatewayStoragePreference(authzToken, gatewayId, 
resourceId);
+        } else {
+            logger.debug(
+                    "Gateway resource profile does not exist for gateway {}, 
will check if user preference exists",
+                    gatewayId);
+        }
+
+        String loginUserName;
+        boolean loginFromUserPref;
+
+        if (userStoragePref != null
+                && userStoragePref.getLoginUserName() != null
+                && !userStoragePref.getLoginUserName().trim().isEmpty()) {
+            loginUserName = userStoragePref.getLoginUserName();
+            loginFromUserPref = true;
+            logger.debug("Using login username from user storage preference 
for resource {}", resourceId);
+
+        } else if (storagePref != null
+                && storagePref.getLoginUserName() != null
+                && !storagePref.getLoginUserName().trim().isEmpty()) {
+            loginUserName = storagePref.getLoginUserName();
+            loginFromUserPref = false;
+            logger.debug("Using login username from gateway storage preference 
for resource {}", resourceId);
+
+        } else {
+            logger.error("No login username found for storage resource {}", 
resourceId);
+            throw new InvalidRequestException("No login username found for 
storage resource " + resourceId);
+        }
+
+        // Resolve credential token based on where login came from
+        String credentialToken;
+        if (loginFromUserPref) {
+            // Login came from user preference. Use user preference token or 
user profile token
+            if (userStoragePref != null
+                    && 
userStoragePref.getResourceSpecificCredentialStoreToken() != null
+                    && !userStoragePref
+                            .getResourceSpecificCredentialStoreToken()
+                            .trim()
+                            .isEmpty()) {
+                credentialToken = 
userStoragePref.getResourceSpecificCredentialStoreToken();
+                logger.debug("Using login username from user preference for 
resource {}", resourceId);
+
+            } else {
+                UserResourceProfile userResourceProfile = 
getUserResourceProfile(authzToken, userId, gatewayId);
+                if (userResourceProfile == null
+                        || userResourceProfile.getCredentialStoreToken() == 
null
+                        || 
userResourceProfile.getCredentialStoreToken().trim().isEmpty()) {
+                    logger.error("No credential store token found for user {} 
in gateway {}", userId, gatewayId);
+                    throw clientException(
+                            AiravataErrorType.AUTHENTICATION_FAILURE,
+                            "No credential store token found for user " + 
userId + " in gateway " + gatewayId);
+                }
+                credentialToken = 
userResourceProfile.getCredentialStoreToken();
+            }
+        } else {
+            // Login came from gateway preference. Use gateway preference 
token or gateway profile token
+            if (storagePref != null
+                    && storagePref.getResourceSpecificCredentialStoreToken() 
!= null
+                    && !storagePref
+                            .getResourceSpecificCredentialStoreToken()
+                            .trim()
+                            .isEmpty()) {
+                credentialToken = 
storagePref.getResourceSpecificCredentialStoreToken();
+
+            } else {
+                GatewayResourceProfile gatewayResourceProfile = 
getGatewayResourceProfile(authzToken, gatewayId);
+                if (gatewayResourceProfile == null
+                        || gatewayResourceProfile.getCredentialStoreToken() == 
null
+                        || gatewayResourceProfile
+                                .getCredentialStoreToken()
+                                .trim()
+                                .isEmpty()) {
+                    logger.error("No credential store token found for gateway 
{}", gatewayId);
+                    throw clientException(
+                            AiravataErrorType.AUTHENTICATION_FAILURE,
+                            "No credential store token found for gateway " + 
gatewayId);
+                }
+                credentialToken = 
gatewayResourceProfile.getCredentialStoreToken();
+            }
+        }
+
+        AgentAdaptor adaptor = AdaptorSupportImpl.getInstance()
+                .fetchStorageSSHAdaptor(gatewayId, resourceId, 
credentialToken, userId, loginUserName);
+        logger.info("Resolved resource {} as storage resource to fetch storage 
details", resourceId);
+
+        return new StorageInfoContext(loginUserName, credentialToken, adaptor);
+    }
 }
diff --git 
a/airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJAgentAdaptor.java
 
b/airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJAgentAdaptor.java
index 7b715f4cd3..9e9caaefc1 100644
--- 
a/airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJAgentAdaptor.java
+++ 
b/airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJAgentAdaptor.java
@@ -51,6 +51,7 @@ import 
org.apache.airavata.model.appcatalog.computeresource.ComputeResourceDescr
 import 
org.apache.airavata.model.appcatalog.computeresource.JobSubmissionInterface;
 import 
org.apache.airavata.model.appcatalog.computeresource.JobSubmissionProtocol;
 import org.apache.airavata.model.appcatalog.computeresource.SSHJobSubmission;
+import org.apache.airavata.model.appcatalog.storageresource.StorageVolumeInfo;
 import org.apache.airavata.model.credential.store.SSHCredential;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -593,4 +594,134 @@ public class SSHJAgentAdaptor implements AgentAdaptor {
         }
         return j == p.length();
     }
+
+    @Override
+    public StorageVolumeInfo getStorageVolumeInfo(String location) throws 
AgentException {
+        try {
+            String targetLocation = location;
+            if (targetLocation == null || targetLocation.trim().isEmpty()) {
+                CommandOutput homeOutput = executeCommand("echo $HOME", null);
+
+                if (homeOutput.getExitCode() != 0
+                        || homeOutput.getStdOut() == null
+                        || homeOutput.getStdOut().trim().isEmpty()) {
+                    logger.error("Failed to determine user's home directory: 
{}", homeOutput.getStdError());
+                    throw new AgentException("Failed to determine user's home 
directory: " + homeOutput.getStdError());
+                }
+                targetLocation = homeOutput.getStdOut().trim();
+            }
+
+            // Escape location to prevent command injection and handle spaces
+            String escapedLocation = targetLocation.replace("'", "'\"'\"'");
+            String dfCommand = "df -P -T -h '" + escapedLocation + "'";
+            String dfBytesCommand = "df -P -T '" + escapedLocation + "'";
+
+            CommandOutput dfHumanOutput = executeCommand(dfCommand, null);
+            CommandOutput dfBytesOutput = executeCommand(dfBytesCommand, null);
+
+            if (dfHumanOutput.getExitCode() != 0) {
+                logger.error(
+                        "Failed to execute df command for location {}: {}",
+                        targetLocation,
+                        dfHumanOutput.getStdError());
+                throw new AgentException("Failed to execute df command for 
location " + targetLocation + ": "
+                        + dfHumanOutput.getStdError());
+            }
+
+            if (dfBytesOutput.getExitCode() != 0) {
+                logger.error(
+                        "Failed to execute df command for location {}: {}",
+                        targetLocation,
+                        dfBytesOutput.getStdError());
+                throw new AgentException("Failed to execute df command for 
location " + targetLocation + ": "
+                        + dfBytesOutput.getStdError());
+            }
+
+            return parseDfOutput(dfHumanOutput.getStdOut(), 
dfBytesOutput.getStdOut(), targetLocation);
+
+        } catch (Exception e) {
+            logger.error("Error while retrieving storage volume info for 
location " + location, e);
+            throw new AgentException("Error while retrieving storage volume 
info for location " + location, e);
+        }
+    }
+
+    private StorageVolumeInfo parseDfOutput(String dfHumanOutput, String 
dfBytesOutput, String targetLocation)
+            throws AgentException {
+        try {
+            // Parse df -P -T -h output (POSIX format with filesystem type)
+            String[] humanLines = dfHumanOutput.split("\n");
+            String[] bytesLines = dfBytesOutput.split("\n");
+
+            if (humanLines.length < 2 || bytesLines.length < 2) {
+                logger.error(
+                        "Unexpected df output format while parsing storage 
volume info for location {}",
+                        targetLocation);
+                throw new AgentException(
+                        "Unexpected df output format while parsing storage 
volume info for location " + targetLocation);
+            }
+
+            // Skip the header line and get the data line
+            String humanDataLine = humanLines[1].trim();
+            String bytesDataLine = bytesLines[1].trim();
+
+            // Split by whitespace. POSIX format uses fixed width columns 
separated by spaces
+            String[] humanFields = humanDataLine.split("\\s+");
+            String[] bytesFields = bytesDataLine.split("\\s+");
+
+            if (humanFields.length < 7 || bytesFields.length < 7) {
+                logger.error(
+                        "Unexpected df output format - insufficient fields 
while parsing storage volume info for location {}",
+                        targetLocation);
+                throw new AgentException(
+                        "Unexpected df output format - insufficient fields 
while parsing storage volume info for location "
+                                + targetLocation);
+            }
+
+            String filesystemType = humanFields[1]; // ext4, xfs, etc.
+            String totalSizeHuman = humanFields[2];
+            String usedSizeHuman = humanFields[3];
+            String availableSizeHuman = humanFields[4];
+            String capacityStr = humanFields[5].replace("%", "");
+
+            // If Mount point contains spaces
+            StringBuilder mountPointBuilder = new StringBuilder();
+            for (int i = 6; i < humanFields.length; i++) {
+                if (i > 6) {
+                    mountPointBuilder.append(" ");
+                }
+                mountPointBuilder.append(humanFields[i]);
+            }
+            String mountPoint = mountPointBuilder.toString();
+
+            // Parse bytes output. Same format but in 1024-byte blocks
+            long totalSizeBlocks = Long.parseLong(bytesFields[2]);
+            long usedSizeBlocks = Long.parseLong(bytesFields[3]);
+            long availableSizeBlocks = Long.parseLong(bytesFields[4]);
+
+            // Convert 1024-byte blocks to bytes
+            long totalSizeBytes = totalSizeBlocks * 1024L;
+            long usedSizeBytes = usedSizeBlocks * 1024L;
+            long availableSizeBytes = availableSizeBlocks * 1024L;
+
+            double percentageUsed = Double.parseDouble(capacityStr);
+
+            StorageVolumeInfo volumeInfo = new StorageVolumeInfo();
+            volumeInfo.setTotalSize(totalSizeHuman);
+            volumeInfo.setUsedSize(usedSizeHuman);
+            volumeInfo.setAvailableSize(availableSizeHuman);
+            volumeInfo.setTotalSizeBytes(totalSizeBytes);
+            volumeInfo.setUsedSizeBytes(usedSizeBytes);
+            volumeInfo.setAvailableSizeBytes(availableSizeBytes);
+            volumeInfo.setPercentageUsed(percentageUsed);
+            volumeInfo.setMountPoint(mountPoint);
+            volumeInfo.setFilesystemType(filesystemType);
+
+            return volumeInfo;
+
+        } catch (Exception e) {
+            logger.error("Error parsing df output: {} for location {}", 
e.getMessage(), targetLocation, e);
+            throw new AgentException(
+                    "Error parsing df output: " + e.getMessage() + " for 
location " + targetLocation, e);
+        }
+    }
 }
diff --git 
a/airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJStorageAdaptor.java
 
b/airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJStorageAdaptor.java
index f9a0751b79..b1a6c4e800 100644
--- 
a/airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJStorageAdaptor.java
+++ 
b/airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJStorageAdaptor.java
@@ -108,4 +108,10 @@ public class SSHJStorageAdaptor extends SSHJAgentAdaptor 
implements StorageResou
     public CommandOutput executeCommand(String command, String 
workingDirectory) throws AgentException {
         return super.executeCommand(command, workingDirectory);
     }
+
+    @Override
+    public 
org.apache.airavata.model.appcatalog.storageresource.StorageVolumeInfo 
getStorageVolumeInfo(String location)
+            throws AgentException {
+        return super.getStorageVolumeInfo(location);
+    }
 }
diff --git 
a/airavata-api/src/main/java/org/apache/airavata/helix/agent/ssh/SshAgentAdaptor.java
 
b/airavata-api/src/main/java/org/apache/airavata/helix/agent/ssh/SshAgentAdaptor.java
index a59be3c5fe..40096595b2 100644
--- 
a/airavata-api/src/main/java/org/apache/airavata/helix/agent/ssh/SshAgentAdaptor.java
+++ 
b/airavata-api/src/main/java/org/apache/airavata/helix/agent/ssh/SshAgentAdaptor.java
@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.UUID;
 import org.apache.airavata.agents.api.*;
 import org.apache.airavata.model.appcatalog.computeresource.*;
+import org.apache.airavata.model.appcatalog.storageresource.StorageVolumeInfo;
 import org.apache.airavata.model.credential.store.SSHCredential;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -540,6 +541,12 @@ public class SshAgentAdaptor implements AgentAdaptor {
         throw new AgentException("Operation not implemented");
     }
 
+    @Override
+    public StorageVolumeInfo getStorageVolumeInfo(String location) {
+        throw new UnsupportedOperationException(
+                "Operation not supported by SshAgentAdaptor. Use 
SSHJAgentAdaptor instead.");
+    }
+
     private static class DefaultUserInfo implements UserInfo, 
UIKeyboardInteractive {
 
         private String userName;
diff --git 
a/airavata-api/src/main/java/org/apache/airavata/helix/core/support/adaptor/AdaptorSupportImpl.java
 
b/airavata-api/src/main/java/org/apache/airavata/helix/core/support/adaptor/AdaptorSupportImpl.java
index b5c47b1b05..06d01f39bb 100644
--- 
a/airavata-api/src/main/java/org/apache/airavata/helix/core/support/adaptor/AdaptorSupportImpl.java
+++ 
b/airavata-api/src/main/java/org/apache/airavata/helix/core/support/adaptor/AdaptorSupportImpl.java
@@ -20,7 +20,9 @@
 package org.apache.airavata.helix.core.support.adaptor;
 
 import java.util.Optional;
-import org.apache.airavata.agents.api.*;
+import org.apache.airavata.agents.api.AgentAdaptor;
+import org.apache.airavata.agents.api.AgentException;
+import org.apache.airavata.agents.api.StorageResourceAdaptor;
 import org.apache.airavata.helix.adaptor.SSHJAgentAdaptor;
 import org.apache.airavata.helix.adaptor.SSHJStorageAdaptor;
 import org.apache.airavata.helix.task.api.support.AdaptorSupport;
@@ -131,4 +133,97 @@ public class AdaptorSupportImpl implements AdaptorSupport {
             }
         }
     }
+
+    @Override
+    public AgentAdaptor fetchComputeSSHAdaptor(
+            String gatewayId, String resourceId, String authToken, String 
gatewayUserId, String loginUserName)
+            throws AgentException {
+        String cacheKey = "compute-" + resourceId;
+
+        logger.debug(
+                "Fetching SSH adaptor for compute resource {} with token {} 
for gateway user {} with login username {}",
+                resourceId,
+                authToken,
+                gatewayUserId,
+                loginUserName);
+
+        Optional<AgentAdaptor> adaptorOp = agentStore.getSSHAdaptor(cacheKey, 
authToken, gatewayUserId, loginUserName);
+        if (adaptorOp.isPresent()) {
+            logger.debug(
+                    "Reusing SSH adaptor for gateway {}, compute resource {}, 
gateway user {}, login username {}",
+                    gatewayId,
+                    resourceId,
+                    gatewayUserId,
+                    loginUserName);
+            return adaptorOp.get();
+
+        } else {
+            synchronized (this) {
+                adaptorOp = agentStore.getSSHAdaptor(cacheKey, authToken, 
gatewayUserId, loginUserName);
+                if (adaptorOp.isPresent()) {
+                    return adaptorOp.get();
+
+                } else {
+                    logger.debug(
+                            "Could not find SSH adaptor for gateway {}, 
compute resource {}, gateway user {}, login username {}. Creating new one",
+                            gatewayId,
+                            resourceId,
+                            gatewayUserId,
+                            loginUserName);
+
+                    SSHJAgentAdaptor agentAdaptor = new SSHJAgentAdaptor();
+                    agentAdaptor.init(resourceId, gatewayId, loginUserName, 
authToken);
+
+                    agentStore.putSSHAdaptor(cacheKey, authToken, 
gatewayUserId, loginUserName, agentAdaptor);
+                    return agentAdaptor;
+                }
+            }
+        }
+    }
+
+    @Override
+    public StorageResourceAdaptor fetchStorageSSHAdaptor(
+            String gatewayId, String resourceId, String authToken, String 
gatewayUserId, String loginUserName)
+            throws AgentException {
+        String cacheKey = "storage-" + resourceId;
+
+        logger.debug(
+                "Fetching SSH adaptor for storage resource {} with token {} 
for gateway user {} with login username {}",
+                resourceId,
+                authToken,
+                gatewayUserId,
+                loginUserName);
+
+        Optional<AgentAdaptor> adaptorOp = agentStore.getSSHAdaptor(cacheKey, 
authToken, gatewayUserId, loginUserName);
+        if (adaptorOp.isPresent()) {
+            logger.debug(
+                    "Reusing SSH adaptor for gateway {}, storage resource {}, 
gateway user {}, login username {}",
+                    gatewayId,
+                    resourceId,
+                    gatewayUserId,
+                    loginUserName);
+            return (StorageResourceAdaptor) adaptorOp.get();
+
+        } else {
+            synchronized (this) {
+                adaptorOp = agentStore.getSSHAdaptor(cacheKey, authToken, 
gatewayUserId, loginUserName);
+                if (adaptorOp.isPresent()) {
+                    return (StorageResourceAdaptor) adaptorOp.get();
+                } else {
+                    logger.debug(
+                            "Could not find SSH adaptor for gateway {}, 
storage resource {}, gateway user {}, login username {}. Creating new one",
+                            gatewayId,
+                            resourceId,
+                            gatewayUserId,
+                            loginUserName);
+
+                    SSHJStorageAdaptor storageAdaptor = new 
SSHJStorageAdaptor();
+                    storageAdaptor.init(resourceId, gatewayId, loginUserName, 
authToken);
+
+                    agentStore.putSSHAdaptor(cacheKey, authToken, 
gatewayUserId, loginUserName, storageAdaptor);
+                    return storageAdaptor;
+                }
+            }
+        }
+    }
 }
diff --git 
a/airavata-api/src/main/java/org/apache/airavata/helix/core/support/adaptor/AgentStore.java
 
b/airavata-api/src/main/java/org/apache/airavata/helix/core/support/adaptor/AgentStore.java
index 48d1c04eee..29e0f6399e 100644
--- 
a/airavata-api/src/main/java/org/apache/airavata/helix/core/support/adaptor/AgentStore.java
+++ 
b/airavata-api/src/main/java/org/apache/airavata/helix/core/support/adaptor/AgentStore.java
@@ -40,6 +40,8 @@ public class AgentStore {
             new HashMap<>();
     private final Map<String, Map<DataMovementProtocol, Map<String, 
Map<String, StorageResourceAdaptor>>>>
             storageAdaptorCache = new HashMap<>();
+    // SSH adaptor cache: resourceId (with compute/storage prefix): auth 
token: gatewayUserId: loginUserName: adaptor
+    private final Map<String, Map<String, Map<String, Map<String, 
AgentAdaptor>>>> sshAdaptorCache = new HashMap<>();
 
     public Optional<AgentAdaptor> getAgentAdaptor(
             String computeResource, JobSubmissionProtocol submissionProtocol, 
String authToken, String userId) {
@@ -111,4 +113,40 @@ public class AgentStore {
                 tokenToUserMap.computeIfAbsent(authToken, k -> new 
HashMap<>());
         userToAdaptorMap.put(userId, storageResourceAdaptor);
     }
+
+    public Optional<AgentAdaptor> getSSHAdaptor(
+            String resourceId, String authToken, String gatewayUserId, String 
loginUserName) {
+        Map<String, Map<String, Map<String, AgentAdaptor>>> 
tokenToGatewayUserMap = sshAdaptorCache.get(resourceId);
+
+        if (tokenToGatewayUserMap != null) {
+            Map<String, Map<String, AgentAdaptor>> gatewayUserToLoginUserMap = 
tokenToGatewayUserMap.get(authToken);
+
+            if (gatewayUserToLoginUserMap != null) {
+                Map<String, AgentAdaptor> loginUserToAdaptorMap = 
gatewayUserToLoginUserMap.get(gatewayUserId);
+
+                if (loginUserToAdaptorMap != null) {
+                    return 
Optional.ofNullable(loginUserToAdaptorMap.get(loginUserName));
+                } else {
+                    return Optional.empty();
+                }
+            } else {
+                return Optional.empty();
+            }
+        } else {
+            return Optional.empty();
+        }
+    }
+
+    public void putSSHAdaptor(
+            String resourceId, String authToken, String gatewayUserId, String 
loginUserName, AgentAdaptor adaptor) {
+
+        Map<String, Map<String, Map<String, AgentAdaptor>>> 
tokenToGatewayUserMap =
+                sshAdaptorCache.computeIfAbsent(resourceId, k -> new 
HashMap<>());
+        Map<String, Map<String, AgentAdaptor>> gatewayUserToLoginUserMap =
+                tokenToGatewayUserMap.computeIfAbsent(authToken, k -> new 
HashMap<>());
+        Map<String, AgentAdaptor> loginUserToAdaptorMap =
+                gatewayUserToLoginUserMap.computeIfAbsent(gatewayUserId, k -> 
new HashMap<>());
+
+        loginUserToAdaptorMap.put(loginUserName, adaptor);
+    }
 }
diff --git 
a/airavata-api/src/main/java/org/apache/airavata/helix/task/api/support/AdaptorSupport.java
 
b/airavata-api/src/main/java/org/apache/airavata/helix/task/api/support/AdaptorSupport.java
index d87e9dec9f..404d03175b 100644
--- 
a/airavata-api/src/main/java/org/apache/airavata/helix/task/api/support/AdaptorSupport.java
+++ 
b/airavata-api/src/main/java/org/apache/airavata/helix/task/api/support/AdaptorSupport.java
@@ -19,7 +19,9 @@
 */
 package org.apache.airavata.helix.task.api.support;
 
-import org.apache.airavata.agents.api.*;
+import org.apache.airavata.agents.api.AgentAdaptor;
+import org.apache.airavata.agents.api.AgentException;
+import org.apache.airavata.agents.api.StorageResourceAdaptor;
 import 
org.apache.airavata.model.appcatalog.computeresource.JobSubmissionProtocol;
 import org.apache.airavata.model.data.movement.DataMovementProtocol;
 
@@ -30,13 +32,21 @@ import 
org.apache.airavata.model.data.movement.DataMovementProtocol;
  * @since 1.0.0-SNAPSHOT
  */
 public interface AdaptorSupport {
-    public void initializeAdaptor();
+    void initializeAdaptor();
 
-    public AgentAdaptor fetchAdaptor(
+    AgentAdaptor fetchAdaptor(
             String gatewayId, String computeResource, JobSubmissionProtocol 
protocol, String authToken, String userId)
             throws Exception;
 
-    public StorageResourceAdaptor fetchStorageAdaptor(
+    StorageResourceAdaptor fetchStorageAdaptor(
             String gatewayId, String storageResourceId, DataMovementProtocol 
protocol, String authToken, String userId)
             throws AgentException;
+
+    AgentAdaptor fetchComputeSSHAdaptor(
+            String gatewayId, String resourceId, String authToken, String 
gatewayUserId, String loginUserName)
+            throws AgentException;
+
+    StorageResourceAdaptor fetchStorageSSHAdaptor(
+            String gatewayId, String resourceId, String authToken, String 
gatewayUserId, String loginUserName)
+            throws AgentException;
 }
diff --git a/dev-tools/airavata-python-sdk/airavata/api/Airavata-remote 
b/dev-tools/airavata-python-sdk/airavata/api/Airavata-remote
index 31d725c53c..173421d374 100755
--- a/dev-tools/airavata-python-sdk/airavata/api/Airavata-remote
+++ b/dev-tools/airavata-python-sdk/airavata/api/Airavata-remote
@@ -217,6 +217,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == '--help':
     print('  string saveParsingTemplate(AuthzToken authzToken, ParsingTemplate 
parsingTemplate)')
     print('  bool removeParsingTemplate(AuthzToken authzToken, string 
templateId, string gatewayId)')
     print('   listAllParsingTemplates(AuthzToken authzToken, string 
gatewayId)')
+    print('  StorageVolumeInfo getResourceStorageInfo(AuthzToken authzToken, 
string resourceId, string location)')
     print('  string getAPIVersion()')
     print('')
     sys.exit(0)
@@ -1455,6 +1456,12 @@ elif cmd == 'listAllParsingTemplates':
         sys.exit(1)
     pp.pprint(client.listAllParsingTemplates(eval(args[0]), args[1],))
 
+elif cmd == 'getResourceStorageInfo':
+    if len(args) != 3:
+        print('getResourceStorageInfo requires 3 args')
+        sys.exit(1)
+    pp.pprint(client.getResourceStorageInfo(eval(args[0]), args[1], args[2],))
+
 elif cmd == 'getAPIVersion':
     if len(args) != 0:
         print('getAPIVersion requires 0 args')
diff --git a/dev-tools/airavata-python-sdk/airavata/api/Airavata.py 
b/dev-tools/airavata-python-sdk/airavata/api/Airavata.py
index 9ee2757f6f..f027db0933 100644
--- a/dev-tools/airavata-python-sdk/airavata/api/Airavata.py
+++ b/dev-tools/airavata-python-sdk/airavata/api/Airavata.py
@@ -3842,6 +3842,23 @@ class Iface(airavata.base.api.BaseAPI.Iface):
         """
         pass
 
+    def getResourceStorageInfo(self, authzToken: 
airavata.model.security.ttypes.AuthzToken, resourceId: str, location: str) -> 
airavata.model.appcatalog.storageresource.ttypes.StorageVolumeInfo:
+        """
+        Get storage volume information for a compute or storage resource.
+
+        @param authzToken
+        @param resourceId Can be either a compute resource ID or storage 
resource ID
+        @param location Optional path/mount point. If null/empty, defaults to 
user's home directory ($HOME)
+        @return StorageVolumeInfo containing disk usage information
+
+        Parameters:
+         - authzToken
+         - resourceId
+         - location
+
+        """
+        pass
+
 
 class Client(airavata.base.api.BaseAPI.Client, Iface):
     def __init__(self, iprot, oprot=None):
@@ -14188,6 +14205,57 @@ class Client(airavata.base.api.BaseAPI.Client, Iface):
             raise result.ae
         raise TApplicationException(TApplicationException.MISSING_RESULT, 
"listAllParsingTemplates failed: unknown result")
 
+    def getResourceStorageInfo(self, authzToken: 
airavata.model.security.ttypes.AuthzToken, resourceId: str, location: str) -> 
airavata.model.appcatalog.storageresource.ttypes.StorageVolumeInfo:
+        """
+        Get storage volume information for a compute or storage resource.
+
+        @param authzToken
+        @param resourceId Can be either a compute resource ID or storage 
resource ID
+        @param location Optional path/mount point. If null/empty, defaults to 
user's home directory ($HOME)
+        @return StorageVolumeInfo containing disk usage information
+
+        Parameters:
+         - authzToken
+         - resourceId
+         - location
+
+        """
+        self.send_getResourceStorageInfo(authzToken, resourceId, location)
+        return self.recv_getResourceStorageInfo()
+
+    def send_getResourceStorageInfo(self, authzToken: 
airavata.model.security.ttypes.AuthzToken, resourceId: str, location: str):
+        self._oprot.writeMessageBegin('getResourceStorageInfo', 
TMessageType.CALL, self._seqid)
+        args = getResourceStorageInfo_args()
+        args.authzToken = authzToken
+        args.resourceId = resourceId
+        args.location = location
+        args.write(self._oprot)
+        self._oprot.writeMessageEnd()
+        self._oprot.trans.flush()
+
+    def recv_getResourceStorageInfo(self) -> 
airavata.model.appcatalog.storageresource.ttypes.StorageVolumeInfo:
+        iprot = self._iprot
+        (fname, mtype, rseqid) = iprot.readMessageBegin()
+        if mtype == TMessageType.EXCEPTION:
+            x = TApplicationException()
+            x.read(iprot)
+            iprot.readMessageEnd()
+            raise x
+        result = getResourceStorageInfo_result()
+        result.read(iprot)
+        iprot.readMessageEnd()
+        if result.success is not None:
+            return result.success
+        if result.ire is not None:
+            raise result.ire
+        if result.ace is not None:
+            raise result.ace
+        if result.ase is not None:
+            raise result.ase
+        if result.ae is not None:
+            raise result.ae
+        raise TApplicationException(TApplicationException.MISSING_RESULT, 
"getResourceStorageInfo failed: unknown result")
+
 
 class Processor(airavata.base.api.BaseAPI.Processor, Iface, TProcessor):
     def __init__(self, handler):
@@ -14385,6 +14453,7 @@ class Processor(airavata.base.api.BaseAPI.Processor, 
Iface, TProcessor):
         self._processMap["saveParsingTemplate"] = 
Processor.process_saveParsingTemplate
         self._processMap["removeParsingTemplate"] = 
Processor.process_removeParsingTemplate
         self._processMap["listAllParsingTemplates"] = 
Processor.process_listAllParsingTemplates
+        self._processMap["getResourceStorageInfo"] = 
Processor.process_getResourceStorageInfo
         self._on_message_begin = None
 
     def on_message_begin(self, func):
@@ -21201,6 +21270,41 @@ class Processor(airavata.base.api.BaseAPI.Processor, 
Iface, TProcessor):
         oprot.writeMessageEnd()
         oprot.trans.flush()
 
+    def process_getResourceStorageInfo(self, seqid, iprot, oprot):
+        args = getResourceStorageInfo_args()
+        args.read(iprot)
+        iprot.readMessageEnd()
+        result = getResourceStorageInfo_result()
+        try:
+            result.success = 
self._handler.getResourceStorageInfo(args.authzToken, args.resourceId, 
args.location)
+            msg_type = TMessageType.REPLY
+        except TTransport.TTransportException:
+            raise
+        except airavata.api.error.ttypes.InvalidRequestException as ire:
+            msg_type = TMessageType.REPLY
+            result.ire = ire
+        except airavata.api.error.ttypes.AiravataClientException as ace:
+            msg_type = TMessageType.REPLY
+            result.ace = ace
+        except airavata.api.error.ttypes.AiravataSystemException as ase:
+            msg_type = TMessageType.REPLY
+            result.ase = ase
+        except airavata.api.error.ttypes.AuthorizationException as ae:
+            msg_type = TMessageType.REPLY
+            result.ae = ae
+        except TApplicationException as ex:
+            logging.exception('TApplication exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = ex
+        except Exception:
+            logging.exception('Unexpected exception in handler')
+            msg_type = TMessageType.EXCEPTION
+            result = 
TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error')
+        oprot.writeMessageBegin("getResourceStorageInfo", msg_type, seqid)
+        result.write(oprot)
+        oprot.writeMessageEnd()
+        oprot.trans.flush()
+
 # HELPER FUNCTIONS AND STRUCTURES
 
 
@@ -60999,5 +61103,210 @@ listAllParsingTemplates_result.thrift_spec = (
     (3, TType.STRUCT, 'ase', 
[airavata.api.error.ttypes.AiravataSystemException, None], None, ),  # 3
     (4, TType.STRUCT, 'ae', [airavata.api.error.ttypes.AuthorizationException, 
None], None, ),  # 4
 )
+
+
+class getResourceStorageInfo_args(object):
+    """
+    Attributes:
+     - authzToken
+     - resourceId
+     - location
+
+    """
+    thrift_spec: typing.Any = None
+
+
+    def __init__(self, authzToken: airavata.model.security.ttypes.AuthzToken = 
None, resourceId: str = None, location: typing.Optional[str] = None,):
+        self.authzToken: airavata.model.security.ttypes.AuthzToken = authzToken
+        self.resourceId: str = resourceId
+        self.location: typing.Optional[str] = location
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, 
TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRUCT:
+                    self.authzToken = 
airavata.model.security.ttypes.AuthzToken()
+                    self.authzToken.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.resourceId = iprot.readString().decode('utf-8', 
errors='replace') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.location = iprot.readString().decode('utf-8', 
errors='replace') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        self.validate()
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, 
self.thrift_spec]))
+            return
+        oprot.writeStructBegin('getResourceStorageInfo_args')
+        if self.authzToken is not None:
+            oprot.writeFieldBegin('authzToken', TType.STRUCT, 1)
+            self.authzToken.write(oprot)
+            oprot.writeFieldEnd()
+        if self.resourceId is not None:
+            oprot.writeFieldBegin('resourceId', TType.STRING, 2)
+            oprot.writeString(self.resourceId.encode('utf-8') if 
sys.version_info[0] == 2 else self.resourceId)
+            oprot.writeFieldEnd()
+        if self.location is not None:
+            oprot.writeFieldBegin('location', TType.STRING, 3)
+            oprot.writeString(self.location.encode('utf-8') if 
sys.version_info[0] == 2 else self.location)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.authzToken is None:
+            raise TProtocolException(message='Required field authzToken is 
unset!')
+        if self.resourceId is None:
+            raise TProtocolException(message='Required field resourceId is 
unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == 
other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(getResourceStorageInfo_args)
+getResourceStorageInfo_args.thrift_spec = (
+    None,  # 0
+    (1, TType.STRUCT, 'authzToken', 
[airavata.model.security.ttypes.AuthzToken, None], None, ),  # 1
+    (2, TType.STRING, 'resourceId', 'UTF8', None, ),  # 2
+    (3, TType.STRING, 'location', 'UTF8', None, ),  # 3
+)
+
+
+class getResourceStorageInfo_result(object):
+    """
+    Attributes:
+     - success
+     - ire
+     - ace
+     - ase
+     - ae
+
+    """
+    thrift_spec: typing.Any = None
+
+
+    def __init__(self, success: 
typing.Optional[airavata.model.appcatalog.storageresource.ttypes.StorageVolumeInfo]
 = None, ire: 
typing.Optional[airavata.api.error.ttypes.InvalidRequestException] = None, ace: 
typing.Optional[airavata.api.error.ttypes.AiravataClientException] = None, ase: 
typing.Optional[airavata.api.error.ttypes.AiravataSystemException] = None, ae: 
typing.Optional[airavata.api.error.ttypes.AuthorizationException] = None,):
+        self.success: 
typing.Optional[airavata.model.appcatalog.storageresource.ttypes.StorageVolumeInfo]
 = success
+        self.ire: 
typing.Optional[airavata.api.error.ttypes.InvalidRequestException] = ire
+        self.ace: 
typing.Optional[airavata.api.error.ttypes.AiravataClientException] = ace
+        self.ase: 
typing.Optional[airavata.api.error.ttypes.AiravataSystemException] = ase
+        self.ae: 
typing.Optional[airavata.api.error.ttypes.AuthorizationException] = ae
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, 
TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 0:
+                if ftype == TType.STRUCT:
+                    self.success = 
airavata.model.appcatalog.storageresource.ttypes.StorageVolumeInfo()
+                    self.success.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 1:
+                if ftype == TType.STRUCT:
+                    self.ire = 
airavata.api.error.ttypes.InvalidRequestException.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRUCT:
+                    self.ace = 
airavata.api.error.ttypes.AiravataClientException.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRUCT:
+                    self.ase = 
airavata.api.error.ttypes.AiravataSystemException.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.STRUCT:
+                    self.ae = 
airavata.api.error.ttypes.AuthorizationException.read(iprot)
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        self.validate()
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, 
self.thrift_spec]))
+            return
+        oprot.writeStructBegin('getResourceStorageInfo_result')
+        if self.success is not None:
+            oprot.writeFieldBegin('success', TType.STRUCT, 0)
+            self.success.write(oprot)
+            oprot.writeFieldEnd()
+        if self.ire is not None:
+            oprot.writeFieldBegin('ire', TType.STRUCT, 1)
+            self.ire.write(oprot)
+            oprot.writeFieldEnd()
+        if self.ace is not None:
+            oprot.writeFieldBegin('ace', TType.STRUCT, 2)
+            self.ace.write(oprot)
+            oprot.writeFieldEnd()
+        if self.ase is not None:
+            oprot.writeFieldBegin('ase', TType.STRUCT, 3)
+            self.ase.write(oprot)
+            oprot.writeFieldEnd()
+        if self.ae is not None:
+            oprot.writeFieldBegin('ae', TType.STRUCT, 4)
+            self.ae.write(oprot)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == 
other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
+all_structs.append(getResourceStorageInfo_result)
+getResourceStorageInfo_result.thrift_spec = (
+    (0, TType.STRUCT, 'success', 
[airavata.model.appcatalog.storageresource.ttypes.StorageVolumeInfo, None], 
None, ),  # 0
+    (1, TType.STRUCT, 'ire', 
[airavata.api.error.ttypes.InvalidRequestException, None], None, ),  # 1
+    (2, TType.STRUCT, 'ace', 
[airavata.api.error.ttypes.AiravataClientException, None], None, ),  # 2
+    (3, TType.STRUCT, 'ase', 
[airavata.api.error.ttypes.AiravataSystemException, None], None, ),  # 3
+    (4, TType.STRUCT, 'ae', [airavata.api.error.ttypes.AuthorizationException, 
None], None, ),  # 4
+)
 fix_spec(all_structs)
 del all_structs
diff --git 
a/dev-tools/airavata-python-sdk/airavata/model/appcatalog/storageresource/ttypes.py
 
b/dev-tools/airavata-python-sdk/airavata/model/appcatalog/storageresource/ttypes.py
index b786cf3476..5481af5ae1 100644
--- 
a/dev-tools/airavata-python-sdk/airavata/model/appcatalog/storageresource/ttypes.py
+++ 
b/dev-tools/airavata-python-sdk/airavata/model/appcatalog/storageresource/ttypes.py
@@ -174,6 +174,183 @@ class StorageResourceDescription(object):
 
     def __ne__(self, other):
         return not (self == other)
+
+
+class StorageVolumeInfo(object):
+    """
+    Storage Volume Information
+
+    Contains disk usage information for a filesystem/mount point.
+
+    totalSize: Total size in human-readable format (e.g., "100G", "500M")
+    usedSize: Used size in human-readable format
+    availableSize: Available size in human-readable format
+    totalSizeBytes: Total size in bytes
+    usedSizeBytes: Used size in bytes
+    availableSizeBytes: Available size in bytes
+    percentageUsed: Percentage used
+    mountPoint: Mount point/filesystem path
+    filesystemType: Filesystem type if available
+
+    Attributes:
+     - totalSize
+     - usedSize
+     - availableSize
+     - totalSizeBytes
+     - usedSizeBytes
+     - availableSizeBytes
+     - percentageUsed
+     - mountPoint
+     - filesystemType
+
+    """
+    thrift_spec: typing.Any = None
+
+
+    def __init__(self, totalSize: str = None, usedSize: str = None, 
availableSize: str = None, totalSizeBytes: int = None, usedSizeBytes: int = 
None, availableSizeBytes: int = None, percentageUsed: float = None, mountPoint: 
str = None, filesystemType: typing.Optional[str] = None,):
+        self.totalSize: str = totalSize
+        self.usedSize: str = usedSize
+        self.availableSize: str = availableSize
+        self.totalSizeBytes: int = totalSizeBytes
+        self.usedSizeBytes: int = usedSizeBytes
+        self.availableSizeBytes: int = availableSizeBytes
+        self.percentageUsed: float = percentageUsed
+        self.mountPoint: str = mountPoint
+        self.filesystemType: typing.Optional[str] = filesystemType
+
+    def read(self, iprot):
+        if iprot._fast_decode is not None and isinstance(iprot.trans, 
TTransport.CReadableTransport) and self.thrift_spec is not None:
+            iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec])
+            return
+        iprot.readStructBegin()
+        while True:
+            (fname, ftype, fid) = iprot.readFieldBegin()
+            if ftype == TType.STOP:
+                break
+            if fid == 1:
+                if ftype == TType.STRING:
+                    self.totalSize = iprot.readString().decode('utf-8', 
errors='replace') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 2:
+                if ftype == TType.STRING:
+                    self.usedSize = iprot.readString().decode('utf-8', 
errors='replace') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 3:
+                if ftype == TType.STRING:
+                    self.availableSize = iprot.readString().decode('utf-8', 
errors='replace') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 4:
+                if ftype == TType.I64:
+                    self.totalSizeBytes = iprot.readI64()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 5:
+                if ftype == TType.I64:
+                    self.usedSizeBytes = iprot.readI64()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 6:
+                if ftype == TType.I64:
+                    self.availableSizeBytes = iprot.readI64()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 7:
+                if ftype == TType.DOUBLE:
+                    self.percentageUsed = iprot.readDouble()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 8:
+                if ftype == TType.STRING:
+                    self.mountPoint = iprot.readString().decode('utf-8', 
errors='replace') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            elif fid == 9:
+                if ftype == TType.STRING:
+                    self.filesystemType = iprot.readString().decode('utf-8', 
errors='replace') if sys.version_info[0] == 2 else iprot.readString()
+                else:
+                    iprot.skip(ftype)
+            else:
+                iprot.skip(ftype)
+            iprot.readFieldEnd()
+        iprot.readStructEnd()
+
+    def write(self, oprot):
+        self.validate()
+        if oprot._fast_encode is not None and self.thrift_spec is not None:
+            oprot.trans.write(oprot._fast_encode(self, [self.__class__, 
self.thrift_spec]))
+            return
+        oprot.writeStructBegin('StorageVolumeInfo')
+        if self.totalSize is not None:
+            oprot.writeFieldBegin('totalSize', TType.STRING, 1)
+            oprot.writeString(self.totalSize.encode('utf-8') if 
sys.version_info[0] == 2 else self.totalSize)
+            oprot.writeFieldEnd()
+        if self.usedSize is not None:
+            oprot.writeFieldBegin('usedSize', TType.STRING, 2)
+            oprot.writeString(self.usedSize.encode('utf-8') if 
sys.version_info[0] == 2 else self.usedSize)
+            oprot.writeFieldEnd()
+        if self.availableSize is not None:
+            oprot.writeFieldBegin('availableSize', TType.STRING, 3)
+            oprot.writeString(self.availableSize.encode('utf-8') if 
sys.version_info[0] == 2 else self.availableSize)
+            oprot.writeFieldEnd()
+        if self.totalSizeBytes is not None:
+            oprot.writeFieldBegin('totalSizeBytes', TType.I64, 4)
+            oprot.writeI64(self.totalSizeBytes)
+            oprot.writeFieldEnd()
+        if self.usedSizeBytes is not None:
+            oprot.writeFieldBegin('usedSizeBytes', TType.I64, 5)
+            oprot.writeI64(self.usedSizeBytes)
+            oprot.writeFieldEnd()
+        if self.availableSizeBytes is not None:
+            oprot.writeFieldBegin('availableSizeBytes', TType.I64, 6)
+            oprot.writeI64(self.availableSizeBytes)
+            oprot.writeFieldEnd()
+        if self.percentageUsed is not None:
+            oprot.writeFieldBegin('percentageUsed', TType.DOUBLE, 7)
+            oprot.writeDouble(self.percentageUsed)
+            oprot.writeFieldEnd()
+        if self.mountPoint is not None:
+            oprot.writeFieldBegin('mountPoint', TType.STRING, 8)
+            oprot.writeString(self.mountPoint.encode('utf-8') if 
sys.version_info[0] == 2 else self.mountPoint)
+            oprot.writeFieldEnd()
+        if self.filesystemType is not None:
+            oprot.writeFieldBegin('filesystemType', TType.STRING, 9)
+            oprot.writeString(self.filesystemType.encode('utf-8') if 
sys.version_info[0] == 2 else self.filesystemType)
+            oprot.writeFieldEnd()
+        oprot.writeFieldStop()
+        oprot.writeStructEnd()
+
+    def validate(self):
+        if self.totalSize is None:
+            raise TProtocolException(message='Required field totalSize is 
unset!')
+        if self.usedSize is None:
+            raise TProtocolException(message='Required field usedSize is 
unset!')
+        if self.availableSize is None:
+            raise TProtocolException(message='Required field availableSize is 
unset!')
+        if self.totalSizeBytes is None:
+            raise TProtocolException(message='Required field totalSizeBytes is 
unset!')
+        if self.usedSizeBytes is None:
+            raise TProtocolException(message='Required field usedSizeBytes is 
unset!')
+        if self.availableSizeBytes is None:
+            raise TProtocolException(message='Required field 
availableSizeBytes is unset!')
+        if self.percentageUsed is None:
+            raise TProtocolException(message='Required field percentageUsed is 
unset!')
+        if self.mountPoint is None:
+            raise TProtocolException(message='Required field mountPoint is 
unset!')
+        return
+
+    def __repr__(self):
+        L = ['%s=%r' % (key, value)
+             for key, value in self.__dict__.items()]
+        return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
+
+    def __eq__(self, other):
+        return isinstance(other, self.__class__) and self.__dict__ == 
other.__dict__
+
+    def __ne__(self, other):
+        return not (self == other)
 all_structs.append(StorageResourceDescription)
 StorageResourceDescription.thrift_spec = (
     None,  # 0
@@ -185,5 +362,18 @@ StorageResourceDescription.thrift_spec = (
     (6, TType.I64, 'creationTime', None, None, ),  # 6
     (7, TType.I64, 'updateTime', None, None, ),  # 7
 )
+all_structs.append(StorageVolumeInfo)
+StorageVolumeInfo.thrift_spec = (
+    None,  # 0
+    (1, TType.STRING, 'totalSize', 'UTF8', None, ),  # 1
+    (2, TType.STRING, 'usedSize', 'UTF8', None, ),  # 2
+    (3, TType.STRING, 'availableSize', 'UTF8', None, ),  # 3
+    (4, TType.I64, 'totalSizeBytes', None, None, ),  # 4
+    (5, TType.I64, 'usedSizeBytes', None, None, ),  # 5
+    (6, TType.I64, 'availableSizeBytes', None, None, ),  # 6
+    (7, TType.DOUBLE, 'percentageUsed', None, None, ),  # 7
+    (8, TType.STRING, 'mountPoint', 'UTF8', None, ),  # 8
+    (9, TType.STRING, 'filesystemType', 'UTF8', None, ),  # 9
+)
 fix_spec(all_structs)
 del all_structs
diff --git 
a/dev-tools/airavata-python-sdk/airavata_sdk/clients/api_server_client.py 
b/dev-tools/airavata-python-sdk/airavata_sdk/clients/api_server_client.py
index cf6fd2edaa..08c9e15ee0 100644
--- a/dev-tools/airavata-python-sdk/airavata_sdk/clients/api_server_client.py
+++ b/dev-tools/airavata-python-sdk/airavata_sdk/clients/api_server_client.py
@@ -113,6 +113,7 @@ class APIServerClient:
         self.get_all_storage_resource_names = 
self.client.getAllStorageResourceNames
         self.update_storage_resource = self.client.updateStorageResource
         self.delete_storage_resource = self.client.deleteStorageResource
+        self.get_resource_storage_info = self.client.getResourceStorageInfo
         self.add_local_submission_details = 
self.client.addLocalSubmissionDetails
         self.update_local_submission_details = 
self.client.updateLocalSubmissionDetails
         self.get_local_job_submission = self.client.getLocalJobSubmission
diff --git a/dev-tools/airavata-python-sdk/pyproject.toml 
b/dev-tools/airavata-python-sdk/pyproject.toml
index 1323513c0b..601b05f601 100644
--- a/dev-tools/airavata-python-sdk/pyproject.toml
+++ b/dev-tools/airavata-python-sdk/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "airavata-python-sdk"
-version = "2.2.4"
+version = "2.2.5"
 description = "Apache Airavata Python SDK"
 readme = "README.md"
 license = "Apache-2.0"
diff --git a/thrift-interface-descriptions/airavata-apis/airavata_api.thrift 
b/thrift-interface-descriptions/airavata-apis/airavata_api.thrift
index 122a2e6935..c5a95d325d 100644
--- a/thrift-interface-descriptions/airavata-apis/airavata_api.thrift
+++ b/thrift-interface-descriptions/airavata-apis/airavata_api.thrift
@@ -3610,7 +3610,24 @@ service Airavata extends base_api.BaseAPI {
                                                                                
           2: airavata_errors.AiravataClientException ace,
                                                                                
           3: airavata_errors.AiravataSystemException ase,
                                                                                
           4: airavata_errors.AuthorizationException ae);
- //
- //End of API
- }
+
+ /**
+  * Get storage volume information for a compute or storage resource.
+  *
+  * @param authzToken
+  * @param resourceId Can be either a compute resource ID or storage resource 
ID
+  * @param location Optional path/mount point. If null/empty, defaults to 
user's home directory ($HOME)
+  * @return StorageVolumeInfo containing disk usage information
+  */
+ storage_resource_model.StorageVolumeInfo getResourceStorageInfo(1: required 
security_model.AuthzToken authzToken,
+                                                                 2: required 
string resourceId,
+                                                                 3: optional 
string location)
+                                                                 throws (1: 
airavata_errors.InvalidRequestException ire,
+                                                                         2: 
airavata_errors.AiravataClientException ace,
+                                                                         3: 
airavata_errors.AiravataSystemException ase,
+                                                                         4: 
airavata_errors.AuthorizationException ae)
+
+//
+//End of API
+}
 
diff --git 
a/thrift-interface-descriptions/data-models/storage_resource_model.thrift 
b/thrift-interface-descriptions/data-models/storage_resource_model.thrift
index f6b65d80c4..8b41079379 100644
--- a/thrift-interface-descriptions/data-models/storage_resource_model.thrift
+++ b/thrift-interface-descriptions/data-models/storage_resource_model.thrift
@@ -52,3 +52,30 @@ struct StorageResourceDescription {
     6: optional i64 creationTime,
     7: optional i64 updateTime,
 }
+
+/**
+ * Storage Volume Information
+ *
+ * Contains disk usage information for a filesystem/mount point.
+ *
+ * totalSize: Total size in human-readable format (e.g., "100G", "500M")
+ * usedSize: Used size in human-readable format
+ * availableSize: Available size in human-readable format
+ * totalSizeBytes: Total size in bytes
+ * usedSizeBytes: Used size in bytes
+ * availableSizeBytes: Available size in bytes
+ * percentageUsed: Percentage used
+ * mountPoint: Mount point/filesystem path
+ * filesystemType: Filesystem type if available
+ */
+struct StorageVolumeInfo {
+    1: required string totalSize,
+    2: required string usedSize,
+    3: required string availableSize,
+    4: required i64 totalSizeBytes,
+    5: required i64 usedSizeBytes,
+    6: required i64 availableSizeBytes,
+    7: required double percentageUsed,
+    8: required string mountPoint,
+    9: optional string filesystemType,
+}
diff --git a/thrift-interface-descriptions/generate-thrift-stubs.sh 
b/thrift-interface-descriptions/generate-thrift-stubs.sh
index f2608759c3..94b191ccb4 100755
--- a/thrift-interface-descriptions/generate-thrift-stubs.sh
+++ b/thrift-interface-descriptions/generate-thrift-stubs.sh
@@ -46,7 +46,7 @@ then
        exit 0
 fi
 
-REQUIRED_THRIFT_VERSION='0.21.0'
+REQUIRED_THRIFT_VERSION='0.22.0'
 SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 AIRAVATA_DIR=`dirname "$SCRIPT_DIR"`
 
@@ -76,6 +76,7 @@ setup() {
     REGISTRY_THRIFT_FILE="${THRIFTDIR}/service-cpis/registry-api.thrift"
     SHARING_API_THRIFT_FILE="${THRIFTDIR}/service-cpis/sharing_cpi.thrift"
     
PROFILE_SERVICE_THRIFT_FILE="${THRIFTDIR}/service-cpis/profile-service-cpi.thrift"
+    AIRAVATA_API_THRIFT_FILE="${THRIFTDIR}/airavata-apis/airavata_api.thrift"
 
     PHP_THRIFT_FILE="${THRIFTDIR}/stubs_php.thrift"
     JAVA_THRIFT_FILE="${THRIFTDIR}/stubs_java.thrift"

Reply via email to