This is an automated email from the ASF dual-hosted git repository.
lahirujayathilake 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 12cbf58b36 Storage directory size fetching api (#575)
12cbf58b36 is described below
commit 12cbf58b3649fe3430e47c17934f946b26646c10
Author: Dimuthu Wannipurage <[email protected]>
AuthorDate: Tue Nov 11 17:01:45 2025 -0500
Storage directory size fetching api (#575)
* Making experiment data path relative when the destination and source
storage are different.
* Enabling storage directory size listing api
* Adding storage directory size listing api to python sdk
* bump up the sdk version and spotless apply
---------
Co-authored-by: lahiruj <[email protected]>
---
.../apache/airavata/agents/api/AgentAdaptor.java | 3 +
.../api/server/handler/AiravataServerHandler.java | 76 +++++
.../airavata/helix/adaptor/SSHJAgentAdaptor.java | 49 ++++
.../airavata/helix/agent/ssh/SshAgentAdaptor.java | 7 +
.../helix/impl/task/staging/DataStagingTask.java | 2 +
.../impl/task/staging/OutputDataStagingTask.java | 6 +-
.../airavata/api/Airavata-remote | 7 +
.../airavata-python-sdk/airavata/api/Airavata.py | 325 ++++++++++++++++++++-
.../airavata-python-sdk/airavata/model/__init__.py | 1 -
.../model/appcatalog/storageresource/ttypes.py | 85 ++++++
.../airavata_experiments/airavata.py | 61 ++--
dev-tools/airavata-python-sdk/pyproject.toml | 2 +-
.../airavata-apis/airavata_api.thrift | 52 ++--
.../data-models/storage_resource_model.thrift | 11 +
14 files changed, 629 insertions(+), 58 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 245cad8c69..36baea7060 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.StorageDirectoryInfo;
import org.apache.airavata.model.appcatalog.storageresource.StorageVolumeInfo;
/**
@@ -59,4 +60,6 @@ public interface AgentAdaptor {
FileMetadata getFileMetadata(String remoteFile) throws AgentException;
StorageVolumeInfo getStorageVolumeInfo(String location) throws
AgentException;
+
+ StorageDirectoryInfo getStorageDirectoryInfo(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 c8a89566b3..4da1eb7494 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
@@ -59,6 +59,7 @@ import
org.apache.airavata.model.appcatalog.groupresourceprofile.GroupComputeRes
import
org.apache.airavata.model.appcatalog.groupresourceprofile.GroupResourceProfile;
import org.apache.airavata.model.appcatalog.parser.Parser;
import org.apache.airavata.model.appcatalog.parser.ParsingTemplate;
+import
org.apache.airavata.model.appcatalog.storageresource.StorageDirectoryInfo;
import
org.apache.airavata.model.appcatalog.storageresource.StorageResourceDescription;
import org.apache.airavata.model.appcatalog.storageresource.StorageVolumeInfo;
import
org.apache.airavata.model.appcatalog.userresourceprofile.UserComputeResourcePreference;
@@ -3919,6 +3920,81 @@ public class AiravataServerHandler implements
Airavata.Iface {
}
}
+ @Override
+ @SecurityCheck
+ public StorageDirectoryInfo getStorageDirectoryInfo(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.getStorageDirectoryInfo(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.
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 9e9caaefc1..2845afb1ef 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.StorageDirectoryInfo;
import org.apache.airavata.model.appcatalog.storageresource.StorageVolumeInfo;
import org.apache.airavata.model.credential.store.SSHCredential;
import org.slf4j.Logger;
@@ -645,6 +646,54 @@ public class SSHJAgentAdaptor implements AgentAdaptor {
}
}
+ @Override
+ public StorageDirectoryInfo getStorageDirectoryInfo(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 duKBytesCommand = "du -sk '" + escapedLocation + "'";
+
+ CommandOutput duKBytesOutput = executeCommand(duKBytesCommand,
null);
+
+ if (duKBytesOutput.getExitCode() != 0) {
+ logger.error(
+ "Failed to execute du -sk command for location {}: {}",
+ targetLocation,
+ duKBytesOutput.getStdError());
+ throw new AgentException("Failed to execute du -sk command for
location " + targetLocation + ": "
+ + duKBytesOutput.getStdError());
+ }
+
+ String outputKbStr = duKBytesOutput.getStdOut().trim();
+ logger.info("OutputKbStr: for du -ku {} is {}", location,
outputKbStr);
+ String numberOfKBytesStr = outputKbStr.split(" ")[0];
+
+ long numberOfKBytes = Long.parseLong(numberOfKBytesStr);
+
+ StorageDirectoryInfo storageDirectoryInfo = new
StorageDirectoryInfo();
+ storageDirectoryInfo.setTotalSizeBytes(numberOfKBytes * 1024);
+ storageDirectoryInfo.setTotalSize(numberOfKBytes + "kb");
+ return storageDirectoryInfo;
+
+ } catch (Exception e) {
+ logger.error("Error while retrieving storage directory info for
location " + location, e);
+ throw new AgentException("Error while retrieving storage directory
info for location " + location, e);
+ }
+ }
+
private StorageVolumeInfo parseDfOutput(String dfHumanOutput, String
dfBytesOutput, String targetLocation)
throws AgentException {
try {
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 40096595b2..8ee4237fd7 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.StorageDirectoryInfo;
import org.apache.airavata.model.appcatalog.storageresource.StorageVolumeInfo;
import org.apache.airavata.model.credential.store.SSHCredential;
import org.slf4j.Logger;
@@ -547,6 +548,12 @@ public class SshAgentAdaptor implements AgentAdaptor {
"Operation not supported by SshAgentAdaptor. Use
SSHJAgentAdaptor instead.");
}
+ @Override
+ public StorageDirectoryInfo getStorageDirectoryInfo(String location)
throws AgentException {
+ 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/impl/task/staging/DataStagingTask.java
b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/staging/DataStagingTask.java
index 568dc82301..dacc55808a 100644
---
a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/staging/DataStagingTask.java
+++
b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/staging/DataStagingTask.java
@@ -202,6 +202,8 @@ public abstract class DataStagingTask extends AiravataTask {
inputPath = (inputPath.endsWith(File.separator) ? inputPath :
inputPath + File.separator);
String experimentDataDir = getProcessModel().getExperimentDataDir();
String filePath;
+ // TODO : This logic is extremely dangerous. This was implemented
expecting the input and output storage are
+ // same.
if (experimentDataDir != null && !experimentDataDir.isEmpty()) {
if (!experimentDataDir.endsWith(File.separator)) {
experimentDataDir += File.separator;
diff --git
a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/staging/OutputDataStagingTask.java
b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/staging/OutputDataStagingTask.java
index ba1e69e0e0..3aab8d3fa6 100644
---
a/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/staging/OutputDataStagingTask.java
+++
b/airavata-api/src/main/java/org/apache/airavata/helix/impl/task/staging/OutputDataStagingTask.java
@@ -94,7 +94,7 @@ public class OutputDataStagingTask extends DataStagingTask {
StoragePreference outputStoragePref =
getTaskContext().getOutputGatewayStorageResourcePreference();
String inputPath =
outputStoragePref.getFileSystemRootLocation();
String destFilePath = buildDestinationFilePath(inputPath,
sourceFileName);
-
+ logger.info("Output storage path for task id " +
getTaskId() + " is " + destFilePath);
destinationURI = new URI(
"file",
outputStoragePref.getLoginUserName(),
@@ -106,6 +106,10 @@ public class OutputDataStagingTask extends DataStagingTask
{
} else {
destinationURI = new
URI(dataStagingTaskModel.getDestination());
+ logger.info(
+ "Output data staging destination for task id {} is
{}",
+ getTaskId(),
+ destinationURI.getPath());
}
if (logger.isDebugEnabled()) {
diff --git a/dev-tools/airavata-python-sdk/airavata/api/Airavata-remote
b/dev-tools/airavata-python-sdk/airavata/api/Airavata-remote
index 173421d374..644d582be5 100755
--- a/dev-tools/airavata-python-sdk/airavata/api/Airavata-remote
+++ b/dev-tools/airavata-python-sdk/airavata/api/Airavata-remote
@@ -218,6 +218,7 @@ if len(sys.argv) <= 1 or sys.argv[1] == '--help':
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(' StorageDirectoryInfo getStorageDirectoryInfo(AuthzToken
authzToken, string resourceId, string location)')
print(' string getAPIVersion()')
print('')
sys.exit(0)
@@ -1462,6 +1463,12 @@ elif cmd == 'getResourceStorageInfo':
sys.exit(1)
pp.pprint(client.getResourceStorageInfo(eval(args[0]), args[1], args[2],))
+elif cmd == 'getStorageDirectoryInfo':
+ if len(args) != 3:
+ print('getStorageDirectoryInfo requires 3 args')
+ sys.exit(1)
+ pp.pprint(client.getStorageDirectoryInfo(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 f027db0933..89f1702a07 100644
--- a/dev-tools/airavata-python-sdk/airavata/api/Airavata.py
+++ b/dev-tools/airavata-python-sdk/airavata/api/Airavata.py
@@ -826,7 +826,7 @@ class Iface(airavata.base.api.BaseAPI.Iface):
@throws org.apache.airavata.model.error.AiravataClientException
The following list of exceptions are thrown which Airavata Client
can take corrective actions to resolve:
-
+
UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata
as a one time administrative
step, then Airavata Registry will not have a provenance area
setup. The client has to follow
gateway registration steps and retry this request.
@@ -914,7 +914,7 @@ class Iface(airavata.base.api.BaseAPI.Iface):
@throws org.apache.airavata.model.error.AiravataClientException
The following list of exceptions are thrown which Airavata Client
can take corrective actions to resolve:
-
+
UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata
as a one time administrative
step, then Airavata Registry will not have a provenance area
setup. The client has to follow
gateway registration steps and retry this request.
@@ -1127,7 +1127,7 @@ class Iface(airavata.base.api.BaseAPI.Iface):
@throws org.apache.airavata.model.error.AiravataClientException
The following list of exceptions are thrown which Airavata Client
can take corrective actions to resolve:
-
+
UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata
as a one time administrative
step, then Airavata Registry will not have a provenance area
setup. The client has to follow
gateway registration steps and retry this request.
@@ -1228,7 +1228,7 @@ class Iface(airavata.base.api.BaseAPI.Iface):
@throws org.apache.airavata.model.error.AiravataClientException
The following list of exceptions are thrown which Airavata Client
can take corrective actions to resolve:
-
+
UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata
as a one time administrative
step, then Airavata Registry will not have a provenance area
setup. The client has to follow
gateway registration steps and retry this request.
@@ -3859,6 +3859,23 @@ class Iface(airavata.base.api.BaseAPI.Iface):
"""
pass
+ def getStorageDirectoryInfo(self, authzToken:
airavata.model.security.ttypes.AuthzToken, resourceId: str, location: str) ->
airavata.model.appcatalog.storageresource.ttypes.StorageDirectoryInfo:
+ """
+ Get storage directory 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 StorageDirectoryInfo containing directory size information
+
+ Parameters:
+ - authzToken
+ - resourceId
+ - location
+
+ """
+ pass
+
class Client(airavata.base.api.BaseAPI.Client, Iface):
def __init__(self, iprot, oprot=None):
@@ -5825,7 +5842,7 @@ class Client(airavata.base.api.BaseAPI.Client, Iface):
@throws org.apache.airavata.model.error.AiravataClientException
The following list of exceptions are thrown which Airavata Client
can take corrective actions to resolve:
-
+
UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata
as a one time administrative
step, then Airavata Registry will not have a provenance area
setup. The client has to follow
gateway registration steps and retry this request.
@@ -6034,7 +6051,7 @@ class Client(airavata.base.api.BaseAPI.Client, Iface):
@throws org.apache.airavata.model.error.AiravataClientException
The following list of exceptions are thrown which Airavata Client
can take corrective actions to resolve:
-
+
UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata
as a one time administrative
step, then Airavata Registry will not have a provenance area
setup. The client has to follow
gateway registration steps and retry this request.
@@ -6526,7 +6543,7 @@ class Client(airavata.base.api.BaseAPI.Client, Iface):
@throws org.apache.airavata.model.error.AiravataClientException
The following list of exceptions are thrown which Airavata Client
can take corrective actions to resolve:
-
+
UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata
as a one time administrative
step, then Airavata Registry will not have a provenance area
setup. The client has to follow
gateway registration steps and retry this request.
@@ -6705,7 +6722,7 @@ class Client(airavata.base.api.BaseAPI.Client, Iface):
@throws org.apache.airavata.model.error.AiravataClientException
The following list of exceptions are thrown which Airavata Client
can take corrective actions to resolve:
-
+
UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata
as a one time administrative
step, then Airavata Registry will not have a provenance area
setup. The client has to follow
gateway registration steps and retry this request.
@@ -14256,6 +14273,57 @@ class Client(airavata.base.api.BaseAPI.Client, Iface):
raise result.ae
raise TApplicationException(TApplicationException.MISSING_RESULT,
"getResourceStorageInfo failed: unknown result")
+ def getStorageDirectoryInfo(self, authzToken:
airavata.model.security.ttypes.AuthzToken, resourceId: str, location: str) ->
airavata.model.appcatalog.storageresource.ttypes.StorageDirectoryInfo:
+ """
+ Get storage directory 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 StorageDirectoryInfo containing directory size information
+
+ Parameters:
+ - authzToken
+ - resourceId
+ - location
+
+ """
+ self.send_getStorageDirectoryInfo(authzToken, resourceId, location)
+ return self.recv_getStorageDirectoryInfo()
+
+ def send_getStorageDirectoryInfo(self, authzToken:
airavata.model.security.ttypes.AuthzToken, resourceId: str, location: str):
+ self._oprot.writeMessageBegin('getStorageDirectoryInfo',
TMessageType.CALL, self._seqid)
+ args = getStorageDirectoryInfo_args()
+ args.authzToken = authzToken
+ args.resourceId = resourceId
+ args.location = location
+ args.write(self._oprot)
+ self._oprot.writeMessageEnd()
+ self._oprot.trans.flush()
+
+ def recv_getStorageDirectoryInfo(self) ->
airavata.model.appcatalog.storageresource.ttypes.StorageDirectoryInfo:
+ iprot = self._iprot
+ (fname, mtype, rseqid) = iprot.readMessageBegin()
+ if mtype == TMessageType.EXCEPTION:
+ x = TApplicationException()
+ x.read(iprot)
+ iprot.readMessageEnd()
+ raise x
+ result = getStorageDirectoryInfo_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,
"getStorageDirectoryInfo failed: unknown result")
+
class Processor(airavata.base.api.BaseAPI.Processor, Iface, TProcessor):
def __init__(self, handler):
@@ -14454,6 +14522,7 @@ class Processor(airavata.base.api.BaseAPI.Processor,
Iface, TProcessor):
self._processMap["removeParsingTemplate"] =
Processor.process_removeParsingTemplate
self._processMap["listAllParsingTemplates"] =
Processor.process_listAllParsingTemplates
self._processMap["getResourceStorageInfo"] =
Processor.process_getResourceStorageInfo
+ self._processMap["getStorageDirectoryInfo"] =
Processor.process_getStorageDirectoryInfo
self._on_message_begin = None
def on_message_begin(self, func):
@@ -21305,6 +21374,41 @@ class Processor(airavata.base.api.BaseAPI.Processor,
Iface, TProcessor):
oprot.writeMessageEnd()
oprot.trans.flush()
+ def process_getStorageDirectoryInfo(self, seqid, iprot, oprot):
+ args = getStorageDirectoryInfo_args()
+ args.read(iprot)
+ iprot.readMessageEnd()
+ result = getStorageDirectoryInfo_result()
+ try:
+ result.success =
self._handler.getStorageDirectoryInfo(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("getStorageDirectoryInfo", msg_type, seqid)
+ result.write(oprot)
+ oprot.writeMessageEnd()
+ oprot.trans.flush()
+
# HELPER FUNCTIONS AND STRUCTURES
@@ -61308,5 +61412,210 @@ getResourceStorageInfo_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 getStorageDirectoryInfo_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('getStorageDirectoryInfo_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(getStorageDirectoryInfo_args)
+getStorageDirectoryInfo_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 getStorageDirectoryInfo_result(object):
+ """
+ Attributes:
+ - success
+ - ire
+ - ace
+ - ase
+ - ae
+
+ """
+ thrift_spec: typing.Any = None
+
+
+ def __init__(self, success:
typing.Optional[airavata.model.appcatalog.storageresource.ttypes.StorageDirectoryInfo]
= 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.StorageDirectoryInfo]
= 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.StorageDirectoryInfo()
+ 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('getStorageDirectoryInfo_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(getStorageDirectoryInfo_result)
+getStorageDirectoryInfo_result.thrift_spec = (
+ (0, TType.STRUCT, 'success',
[airavata.model.appcatalog.storageresource.ttypes.StorageDirectoryInfo, 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/__init__.py
b/dev-tools/airavata-python-sdk/airavata/model/__init__.py
index adefd8e51f..e69de29bb2 100644
--- a/dev-tools/airavata-python-sdk/airavata/model/__init__.py
+++ b/dev-tools/airavata-python-sdk/airavata/model/__init__.py
@@ -1 +0,0 @@
-__all__ = ['ttypes', 'constants']
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 5481af5ae1..6a929fddf4 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
@@ -351,6 +351,85 @@ class StorageVolumeInfo(object):
def __ne__(self, other):
return not (self == other)
+
+
+class StorageDirectoryInfo(object):
+ """
+ Provides Directory Size Information of a given storage
+
+ totalSize: Total size in human-readable format (e.g., "100G", "500M")
+ totalSizeBytes: Total size in bytes
+
+ Attributes:
+ - totalSize
+ - totalSizeBytes
+
+ """
+ thrift_spec: typing.Any = None
+
+
+ def __init__(self, totalSize: str = None, totalSizeBytes: int = None,):
+ self.totalSize: str = totalSize
+ self.totalSizeBytes: int = totalSizeBytes
+
+ 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.I64:
+ self.totalSizeBytes = iprot.readI64()
+ 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('StorageDirectoryInfo')
+ 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.totalSizeBytes is not None:
+ oprot.writeFieldBegin('totalSizeBytes', TType.I64, 2)
+ oprot.writeI64(self.totalSizeBytes)
+ oprot.writeFieldEnd()
+ oprot.writeFieldStop()
+ oprot.writeStructEnd()
+
+ def validate(self):
+ if self.totalSize is None:
+ raise TProtocolException(message='Required field totalSize is
unset!')
+ if self.totalSizeBytes is None:
+ raise TProtocolException(message='Required field totalSizeBytes 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
@@ -375,5 +454,11 @@ StorageVolumeInfo.thrift_spec = (
(8, TType.STRING, 'mountPoint', 'UTF8', None, ), # 8
(9, TType.STRING, 'filesystemType', 'UTF8', None, ), # 9
)
+all_structs.append(StorageDirectoryInfo)
+StorageDirectoryInfo.thrift_spec = (
+ None, # 0
+ (1, TType.STRING, 'totalSize', 'UTF8', None, ), # 1
+ (2, TType.I64, 'totalSizeBytes', None, None, ), # 2
+)
fix_spec(all_structs)
del all_structs
diff --git a/dev-tools/airavata-python-sdk/airavata_experiments/airavata.py
b/dev-tools/airavata-python-sdk/airavata_experiments/airavata.py
index 24961dcfc1..fa596feeaa 100644
--- a/dev-tools/airavata-python-sdk/airavata_experiments/airavata.py
+++ b/dev-tools/airavata-python-sdk/airavata_experiments/airavata.py
@@ -63,7 +63,7 @@ class AiravataOperator:
input_file_name: str,
uploaded_storage_path: str,
) -> str:
-
+
dataProductModel = DataProductModel(
gatewayId=gateway_id,
ownerName=self.user_id,
@@ -88,7 +88,7 @@ class AiravataOperator:
description: str,
gateway_id: str,
) -> ExperimentModel:
-
+
execution_id = self.get_app_interface_id(application_name)
project_id = self.get_project_id(project_name)
return ExperimentModel(
@@ -100,13 +100,13 @@ class AiravataOperator:
experimentType=ExperimentType.SINGLE_APPLICATION,
executionId=execution_id
)
-
+
def get_resource_host_id(self, resource_name):
resources =
self.api_server_client.get_all_compute_resource_names(self.airavata_token)
resource_id = next((k for k in resources if k.startswith(resource_name)),
None)
assert resource_id is not None, f"Compute resource {resource_name} not
found"
return resource_id
-
+
def configure_computation_resource_scheduling(
self,
experiment_model: ExperimentModel,
@@ -154,19 +154,19 @@ class AiravataOperator:
def default_gateway_id(self):
return self.settings.GATEWAY_ID
-
+
def default_gateway_data_store_dir(self):
return self.settings.GATEWAY_DATA_STORE_DIR
-
+
def default_sftp_port(self):
return self.settings.SFTP_PORT
-
+
def default_sr_hostname(self):
return self.settings.STORAGE_RESOURCE_HOST
-
+
def connection_svc_url(self):
return f"{self.settings.API_SERVER_URL}/api/v1"
-
+
def filemgr_svc_url(self):
return self.settings.FILE_SVC_URL
@@ -186,7 +186,7 @@ class AiravataOperator:
"""
return self.api_server_client.get_experiment(self.airavata_token,
experiment_id)
-
+
def get_process_id(self, experiment_id: str) -> str:
"""
Get process id by experiment id
@@ -247,7 +247,7 @@ class AiravataOperator:
grp_id = next((grp.groupResourceProfileId for grp in grps if
grp.groupResourceProfileName == group), None)
assert grp_id is not None, f"Group resource profile {group} not found"
return str(grp_id)
-
+
def get_group_resource_profile(self, group_id: str):
grp =
self.api_server_client.get_group_resource_profile(self.airavata_token,
group_id) # type: ignore
return grp
@@ -327,7 +327,7 @@ class AiravataOperator:
paths = sftp_connector.put(local_files, remote_dir)
logger.info(f"{len(paths)} Local files uploaded to remote dir: %s",
remote_dir)
return paths
-
+
# step = post-staging file upload
elif process_id is not None and agent_ref is not None:
assert len(local_files) == 1, f"Expected 1 file, got {len(local_files)}"
@@ -362,7 +362,7 @@ class AiravataOperator:
# step = unknown
else:
raise ValueError("Invalid arguments for upload_files")
-
+
# file manager service fallback
assert process_id is not None, f"Expected process_id, got {process_id}"
file = local_files[0]
@@ -445,7 +445,7 @@ class AiravataOperator:
f.write(content)
return path.as_posix()
time.sleep(1)
-
+
# file manager service fallback
assert process_id is not None, f"Expected process_id, got {process_id}"
url_path = os.path.join(process_id, remote_file)
@@ -621,6 +621,9 @@ class AiravataOperator:
experiment_name=experiment_name,
)
abs_path = (mount_point / exp_dir.lstrip("/")).as_posix().rstrip("/") + "/"
+ if input_sr_id != output_sr_id:
+ abs_path = self.user_id + "/" + project + "/" + experiment_name
+
print("[AV] exp_dir:", exp_dir)
print("[AV] abs_path:", abs_path)
@@ -641,7 +644,7 @@ class AiravataOperator:
def register_input_file(file: Path) -> str:
return str(self.register_input_file(file.name, input_sr_host,
input_sr_id, gateway_id, file.name, abs_path))
-
+
# set up experiment inputs
print("[AV] Setting up experiment inputs...")
files_to_upload = list[Path]()
@@ -716,16 +719,16 @@ class AiravataOperator:
except Exception as status_err:
if "FAILED" in str(status_err) or "terminal state" in str(status_err):
raise status_err
-
+
try:
process_id = self.get_process_id(ex_id)
except:
pass
-
+
if process_id is None:
time.sleep(2)
wait_count_process += 1
-
+
if process_id is None:
raise Exception(f"[AV] Experiment {experiment_name} timeout waiting for
process to begin")
print(f"[AV] Experiment {experiment_name} EXECUTING with pid:
{process_id}")
@@ -746,16 +749,16 @@ class AiravataOperator:
except Exception as status_err:
if "FAILED" in str(status_err) or "terminal state" in str(status_err):
raise status_err
-
+
try:
job_id, job_state = self.get_task_status(ex_id)
except:
pass
-
+
if job_id in [None, "N/A"]:
time.sleep(2)
wait_count_task += 1
-
+
if job_id in [None, "N/A"]:
raise Exception(f"[AV] Experiment {experiment_name} timeout waiting for
task to begin")
assert job_state is not None, f"Job state is None for job id: {job_id}"
@@ -781,7 +784,7 @@ class AiravataOperator:
status = self.api_server_client.terminate_experiment(
self.airavata_token, experiment_id, self.default_gateway_id())
return status
-
+
def execute_py(self, project: str, libraries: list[str], code: str,
agent_id: str, pid: str, runtime_args: dict, cold_start: bool = True) -> str |
None:
# lambda to send request
try:
@@ -817,7 +820,7 @@ class AiravataOperator:
if data["agentUp"]:
break
time.sleep(1)
-
+
print(f"[av] Agent {agent_id} found! creating environment...")
res = requests.post(f"{self.connection_svc_url()}/setup/env", json={
"agentId": agent_id,
@@ -853,18 +856,18 @@ class AiravataOperator:
response = str(data["responseString"])
break
time.sleep(1)
-
+
print(f"[av] Agent {agent_id} code executed! response: {response}")
return response
-
+
except Exception as e:
print(f"[av] Remote execution failed! {e}")
return None
-
+
def get_available_groups(self, gateway_id: str = "default"):
grps: list[GroupResourceProfile] =
self.api_server_client.get_group_resource_list(self.airavata_token,
gatewayId=gateway_id)
return grps
-
+
def get_available_runtimes(self, group: str, gateway_id: str = "default"):
grps = self.get_available_groups(gateway_id)
grp_id, gcr_prefs, gcr_policies = next(((x.groupResourceProfileId,
x.computePreferences, x.computeResourcePolicies) for x in grps if
str(x.groupResourceProfileName).strip() == group.strip()), (None, None, None))
@@ -896,13 +899,13 @@ class AiravataOperator:
)
runtimes.append(runtime)
return runtimes
-
+
def get_task_status(self, experiment_id: str) -> tuple[str, JobState]:
job_details: dict[str, JobStatus] =
self.api_server_client.get_job_statuses(self.airavata_token, experiment_id) #
type: ignore
job_id = job_state = None
for job_id, v in job_details.items():
job_state = v.jobState
return job_id or "N/A", job_state or JobState.UNKNOWN
-
+
JobState = JobState
diff --git a/dev-tools/airavata-python-sdk/pyproject.toml
b/dev-tools/airavata-python-sdk/pyproject.toml
index 601b05f601..e673998406 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.5"
+version = "2.2.6"
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 c5a95d325d..964a55ceb5 100644
--- a/thrift-interface-descriptions/airavata-apis/airavata_api.thrift
+++ b/thrift-interface-descriptions/airavata-apis/airavata_api.thrift
@@ -20,7 +20,7 @@
/**
* Application Programming Interface definition for Apache Airavata Services.
- * this parent thrift file is contains all service interfaces. The data
models are
+ * this parent thrift file is contains all service interfaces. The data
models are
* described in respective thrift files.
*/
@@ -102,7 +102,7 @@ service Airavata extends base_api.BaseAPI {
*
* @param gateway
* The gateway data model.
- *
+ *
* @return gatewayId
* Th unique identifier of the newly registered gateway.
*
@@ -673,13 +673,13 @@ service Airavata extends base_api.BaseAPI {
*
* @throws org.apache.airavata.model.error.InvalidRequestException
* For any incorrect forming of the request itself.
- *
+ *
* @throws org.apache.airavata.model.error.ExperimentNotFoundException
* If the specified experiment is not previously created, then an
Experiment Not Found Exception is thrown.
- *
+ *
* @throws org.apache.airavata.model.error.AiravataClientException
* The following list of exceptions are thrown which Airavata Client can
take corrective actions to resolve:
- *
+ *
* UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata as
a one time administrative
* step, then Airavata Registry will not have a provenance area
setup. The client has to follow
* gateway registration steps and retry this request.
@@ -811,13 +811,13 @@ service Airavata extends base_api.BaseAPI {
*
* @throws org.apache.airavata.model.error.InvalidRequestException
* For any incorrect forming of the request itself.
- *
+ *
* @throws org.apache.airavata.model.error.ExperimentNotFoundException
* If the specified experiment is not previously created, then an
Experiment Not Found Exception is thrown.
- *
+ *
* @throws org.apache.airavata.model.error.AiravataClientException
* The following list of exceptions are thrown which Airavata Client can
take corrective actions to resolve:
- *
+ *
* UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata as
a one time administrative
* step, then Airavata Registry will not have a provenance area
setup. The client has to follow
* gateway registration steps and retry this request.
@@ -888,13 +888,13 @@ service Airavata extends base_api.BaseAPI {
*
* @throws org.apache.airavata.model.error.InvalidRequestException
* For any incorrect forming of the request itself.
- *
+ *
* @throws org.apache.airavata.model.error.ExperimentNotFoundException
* If the specified experiment is not previously created, then an
Experiment Not Found Exception is thrown.
- *
+ *
* @throws org.apache.airavata.model.error.AiravataClientException
* The following list of exceptions are thrown which Airavata Client can
take corrective actions to resolve:
- *
+ *
* UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata as
a one time administrative
* step, then Airavata Registry will not have a provenance area
setup. The client has to follow
* gateway registration steps and retry this request.
@@ -1076,7 +1076,7 @@ service Airavata extends base_api.BaseAPI {
*
* Clone an Existing Experiment
* Existing specified experiment is cloned and a new name is provided. A
copy of the experiment configuration is made and is persisted with new metadata.
- * The client has to subsequently update this configuration if needed and
launch the cloned experiment.
+ * The client has to subsequently update this configuration if needed and
launch the cloned experiment.
*
* @param newExperimentName
* experiment name that should be used in the cloned experiment
@@ -1094,13 +1094,13 @@ service Airavata extends base_api.BaseAPI {
*
* @throws org.apache.airavata.model.error.InvalidRequestException
* For any incorrect forming of the request itself.
- *
+ *
* @throws org.apache.airavata.model.error.ExperimentNotFoundException
* If the specified experiment is not previously created, then an
Experiment Not Found Exception is thrown.
- *
+ *
* @throws org.apache.airavata.model.error.AiravataClientException
* The following list of exceptions are thrown which Airavata Client can
take corrective actions to resolve:
- *
+ *
* UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata as
a one time administrative
* step, then Airavata Registry will not have a provenance area
setup. The client has to follow
* gateway registration steps and retry this request.
@@ -1196,13 +1196,13 @@ service Airavata extends base_api.BaseAPI {
*
* @throws org.apache.airavata.model.error.InvalidRequestException
* For any incorrect forming of the request itself.
- *
+ *
* @throws org.apache.airavata.model.error.ExperimentNotFoundException
* If the specified experiment is not previously created, then an
Experiment Not Found Exception is thrown.
- *
+ *
* @throws org.apache.airavata.model.error.AiravataClientException
* The following list of exceptions are thrown which Airavata Client can
take corrective actions to resolve:
- *
+ *
* UNKNOWN_GATEWAY_ID - If a Gateway is not registered with Airavata as
a one time administrative
* step, then Airavata Registry will not have a provenance area
setup. The client has to follow
* gateway registration steps and retry this request.
@@ -3627,6 +3627,22 @@ service Airavata extends base_api.BaseAPI {
3:
airavata_errors.AiravataSystemException ase,
4:
airavata_errors.AuthorizationException ae)
+/**
+ * Get storage directory 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 StorageDirectoryInfo containing directory size information
+ */
+ storage_resource_model.StorageDirectoryInfo getStorageDirectoryInfo(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 8b41079379..9d6fa4d70e 100644
--- a/thrift-interface-descriptions/data-models/storage_resource_model.thrift
+++ b/thrift-interface-descriptions/data-models/storage_resource_model.thrift
@@ -79,3 +79,14 @@ struct StorageVolumeInfo {
8: required string mountPoint,
9: optional string filesystemType,
}
+
+/**
+ * Provides Directory Size Information of a given storage
+ *
+ * totalSize: Total size in human-readable format (e.g., "100G", "500M")
+ * totalSizeBytes: Total size in bytes
+ */
+struct StorageDirectoryInfo {
+ 1: required string totalSize,
+ 2: required i64 totalSizeBytes,
+}