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"