This is an automated email from the ASF dual-hosted git repository. yasith pushed a commit to branch feat/airavata-service-layer in repository https://gitbox.apache.org/repos/asf/airavata.git
commit 04d700242c44dffcd046f6d5f8fed77237ade31f Author: yasithdev <[email protected]> AuthorDate: Thu Mar 26 12:51:30 2026 -0500 refactor: move storage info business logic from handler to ResourceService Migrated getResourceStorageInfo, getStorageDirectoryInfo, resolveComputeStorageInfoContext, and resolveStorageStorageInfoContext from AiravataServerHandler into ResourceService. Handler methods are now pure ThriftAdapter one-liners. ResourceService gains a GroupResourceProfileService dependency for the group preference fallback chain. --- .../api/server/handler/AiravataServerHandler.java | 468 +-------------------- .../airavata/service/resource/ResourceService.java | 352 +++++++++++++++- .../service/resource/ResourceServiceTest.java | 4 +- 3 files changed, 372 insertions(+), 452 deletions(-) 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 0f2bdd7898..6f440acc0a 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 @@ -23,11 +23,8 @@ import java.util.*; import java.util.function.BiFunction; import java.util.stream.Collectors; 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; @@ -36,7 +33,6 @@ import org.apache.airavata.common.utils.AiravataUtils; import org.apache.airavata.common.utils.Constants; import org.apache.airavata.common.utils.ServerSettings; import org.apache.airavata.credential.store.server.CredentialStoreServerHandler; -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; @@ -141,6 +137,7 @@ public class AiravataServerHandler implements Airavata.Iface { private final GroupResourceProfileService groupResourceProfileService; private final ParserService parserService; private final ResourceSharingService resourceSharingService; + private final org.apache.airavata.service.ssh.SSHAccountService sshAccountService; public AiravataServerHandler( RegistryServerHandler registryHandler, @@ -166,12 +163,14 @@ public class AiravataServerHandler implements Airavata.Iface { this.gatewayResourceProfileService = new GatewayResourceProfileService(registryHandler); this.notificationService = new NotificationService(registryHandler); this.projectService = new ProjectService(registryHandler, sharingHandler); - this.resourceService = new ResourceService(registryHandler); this.userResourceProfileService = new UserResourceProfileService(registryHandler); this.dataProductService = new DataProductService(registryHandler); this.groupResourceProfileService = new GroupResourceProfileService(registryHandler, sharingHandler); + this.experimentService.setGroupResourceProfileService(this.groupResourceProfileService); + this.resourceService = new ResourceService(registryHandler, this.groupResourceProfileService); this.parserService = new ParserService(registryHandler); this.resourceSharingService = new ResourceSharingService(sharingHandler, registryHandler); + this.sshAccountService = new org.apache.airavata.service.ssh.SSHAccountService(credentialHandler); } public AiravataServerHandler() throws Exception { @@ -1024,10 +1023,8 @@ public class AiravataServerHandler implements Airavata.Iface { public void launchExperiment(AuthzToken authzToken, final String airavataExperimentId, String gatewayId) throws AuthorizationException, AiravataSystemException, TException { logger.info("Launching experiment {}", airavataExperimentId); - ThriftAdapter.executeVoid(authzToken, gatewayId, ctx -> { - List<GroupResourceProfile> groupResourceProfiles = groupResourceProfileService.getGroupResourceList(ctx, gatewayId); - experimentService.launchExperiment(ctx, airavataExperimentId, gatewayId, groupResourceProfiles); - }); + ThriftAdapter.executeVoid(authzToken, gatewayId, + ctx -> experimentService.launchExperiment(ctx, airavataExperimentId, gatewayId)); } // private OrchestratorService.Client getOrchestratorClient() throws TException { @@ -1685,134 +1682,16 @@ public class AiravataServerHandler implements Airavata.Iface { @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); - StorageInfoContext context; - - try { - Optional<ComputeResourceDescription> computeResourceOp = Optional.empty(); - try { - ComputeResourceDescription computeResource = registryHandler.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 = registryHandler.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); - } - - return context.adaptor.getStorageVolumeInfo(location); - - } catch (InvalidRequestException | AiravataClientException e) { - 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); - - AiravataSystemException exception = new AiravataSystemException(); - exception.setAiravataErrorType(AiravataErrorType.INTERNAL_ERROR); - exception.setMessage("Error while retrieving storage volume info. More info: " + e.getMessage()); - throw exception; - } + return ThriftAdapter.execute(authzToken, null, + ctx -> resourceService.getResourceStorageInfo(ctx, resourceId, location)); } @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); - StorageInfoContext context; - - try { - Optional<ComputeResourceDescription> computeResourceOp = Optional.empty(); - try { - ComputeResourceDescription computeResource = registryHandler.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 = registryHandler.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); - } - - return context.adaptor.getStorageDirectoryInfo(location); - - } catch (InvalidRequestException | AiravataClientException e) { - 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); - - AiravataSystemException exception = new AiravataSystemException(); - exception.setAiravataErrorType(AiravataErrorType.INTERNAL_ERROR); - exception.setMessage("Error while retrieving storage volume info. More info: " + e.getMessage()); - throw exception; - } + return ThriftAdapter.execute(authzToken, null, + ctx -> resourceService.getStorageDirectoryInfo(ctx, resourceId, location)); } /** @@ -2542,18 +2421,8 @@ public class AiravataServerHandler implements Airavata.Iface { public boolean doesUserHaveSSHAccount(AuthzToken authzToken, String computeResourceId, String userId) throws InvalidRequestException, AiravataClientException, AiravataSystemException, AuthorizationException, TException { - try { - String gatewayId = authzToken.getClaimsMap().get(Constants.GATEWAY_ID); - return SSHAccountManager.doesUserHaveSSHAccount(gatewayId, computeResourceId, userId); - } catch (Exception e) { - String errorMessage = "Error occurred while checking if [" + userId + "] has an SSH Account on [" - + computeResourceId + "]."; - logger.error(errorMessage, e); - AiravataSystemException exception = new AiravataSystemException(); - exception.setAiravataErrorType(AiravataErrorType.INTERNAL_ERROR); - exception.setMessage(errorMessage + " More info : " + e.getMessage()); - throw exception; - } + return ThriftAdapter.execute(authzToken, null, + ctx -> sshAccountService.doesUserHaveSSHAccount(ctx, computeResourceId, userId)); } @Override @@ -2562,30 +2431,9 @@ public class AiravataServerHandler implements Airavata.Iface { AuthzToken authzToken, String computeResourceId, String airavataCredStoreToken) throws InvalidRequestException, AiravataClientException, AiravataSystemException, AuthorizationException, TException { - String gatewayId = authzToken.getClaimsMap().get(Constants.GATEWAY_ID); - String userId = authzToken.getClaimsMap().get(Constants.USER_NAME); - SSHCredential sshCredential = null; - try { - sshCredential = credentialHandler.getSSHCredential(airavataCredStoreToken, gatewayId); - } catch (Exception e) { - logger.error("Error occurred while retrieving SSH Credential", e); - AiravataSystemException exception = new AiravataSystemException(); - exception.setAiravataErrorType(AiravataErrorType.INTERNAL_ERROR); - exception.setMessage("Error occurred while retrieving SSH Credential. More info : " + e.getMessage()); - throw exception; - } - - try { - return SSHAccountManager.isSSHAccountSetupComplete(gatewayId, computeResourceId, userId, sshCredential); - } catch (Exception e) { - final String msg = - "Error occurred while checking if setup of SSH account is complete for user [" + userId + "]."; - logger.error(msg, e); - AiravataSystemException exception = new AiravataSystemException(); - exception.setAiravataErrorType(AiravataErrorType.INTERNAL_ERROR); - exception.setMessage(msg + " More info : " + e.getMessage()); - throw exception; - } + return ThriftAdapter.execute(authzToken, null, + ctx -> sshAccountService.isSSHSetupCompleteForUserComputeResourcePreference( + ctx, computeResourceId, airavataCredStoreToken)); } @Override @@ -2594,30 +2442,9 @@ public class AiravataServerHandler implements Airavata.Iface { AuthzToken authzToken, String computeResourceId, String userId, String airavataCredStoreToken) throws InvalidRequestException, AiravataClientException, AiravataSystemException, AuthorizationException, TException { - String gatewayId = authzToken.getClaimsMap().get(Constants.GATEWAY_ID); - SSHCredential sshCredential = null; - try { - sshCredential = credentialHandler.getSSHCredential(airavataCredStoreToken, gatewayId); - } catch (Exception e) { - logger.error("Error occurred while retrieving SSH Credential", e); - AiravataSystemException exception = new AiravataSystemException(); - exception.setAiravataErrorType(AiravataErrorType.INTERNAL_ERROR); - exception.setMessage("Error occurred while retrieving SSH Credential. More info : " + e.getMessage()); - throw exception; - } - - try { - UserComputeResourcePreference userComputeResourcePreference = - SSHAccountManager.setupSSHAccount(gatewayId, computeResourceId, userId, sshCredential); - return userComputeResourcePreference; - } catch (Exception e) { - logger.error("Error occurred while automatically setting up SSH account for user [" + userId + "]", e); - AiravataSystemException exception = new AiravataSystemException(); - exception.setAiravataErrorType(AiravataErrorType.INTERNAL_ERROR); - exception.setMessage("Error occurred while automatically setting up SSH account for user [" + userId - + "]. More info : " + e.getMessage()); - throw exception; - } + return ThriftAdapter.execute(authzToken, null, + ctx -> sshAccountService.setupUserComputeResourcePreferencesForSSH( + ctx, computeResourceId, userId, airavataCredStoreToken)); } /** @@ -3284,263 +3111,4 @@ public class AiravataServerHandler implements Airavata.Iface { return ThriftAdapter.execute(authzToken, gatewayId, ctx -> parserService.listAllParsingTemplates(ctx, 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 { - try { - try { - GatewayResourceProfile profile = registryHandler.getGatewayResourceProfile(gatewayId); - return profile != null; - } catch (org.apache.thrift.TApplicationException e) { - logger.error("Gateway resource profile does not exist for gateway: {}", gatewayId, e); - return false; - } - } catch (Exception e) { - 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/service/resource/ResourceService.java b/airavata-api/src/main/java/org/apache/airavata/service/resource/ResourceService.java index 1ffc9726f7..a66866c30d 100644 --- a/airavata-api/src/main/java/org/apache/airavata/service/resource/ResourceService.java +++ b/airavata-api/src/main/java/org/apache/airavata/service/resource/ResourceService.java @@ -1,27 +1,51 @@ package org.apache.airavata.service.resource; +import org.apache.airavata.agents.api.AgentAdaptor; +import org.apache.airavata.agents.api.AgentException; +import org.apache.airavata.helix.core.support.adaptor.AdaptorSupportImpl; import org.apache.airavata.model.appcatalog.computeresource.*; +import org.apache.airavata.model.appcatalog.gatewayprofile.GatewayResourceProfile; +import org.apache.airavata.model.appcatalog.gatewayprofile.StoragePreference; +import org.apache.airavata.model.appcatalog.groupresourceprofile.GroupComputeResourcePreference; +import org.apache.airavata.model.appcatalog.groupresourceprofile.GroupResourceProfile; +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; +import org.apache.airavata.model.appcatalog.userresourceprofile.UserResourceProfile; +import org.apache.airavata.model.appcatalog.userresourceprofile.UserStoragePreference; import org.apache.airavata.model.data.movement.DMType; import org.apache.airavata.model.data.movement.GridFTPDataMovement; import org.apache.airavata.model.data.movement.LOCALDataMovement; import org.apache.airavata.model.data.movement.SCPDataMovement; import org.apache.airavata.model.data.movement.UnicoreDataMovement; +import org.apache.airavata.model.error.AiravataErrorType; import org.apache.airavata.registry.api.service.handler.RegistryServerHandler; +import org.apache.airavata.service.context.RequestContext; +import org.apache.airavata.service.exception.ServiceAuthorizationException; import org.apache.airavata.service.exception.ServiceException; +import org.apache.airavata.service.groupprofile.GroupResourceProfileService; +import org.apache.airavata.service.resourceprofile.GatewayResourceProfileService; +import org.apache.airavata.service.resourceprofile.UserResourceProfileService; +import org.apache.thrift.TApplicationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; import java.util.Map; +import java.util.Optional; public class ResourceService { private static final Logger logger = LoggerFactory.getLogger(ResourceService.class); private final RegistryServerHandler registryHandler; + private final GroupResourceProfileService groupResourceProfileService; - public ResourceService(RegistryServerHandler registryHandler) { + public ResourceService(RegistryServerHandler registryHandler, + GroupResourceProfileService groupResourceProfileService) { this.registryHandler = registryHandler; + this.groupResourceProfileService = groupResourceProfileService; } // ------------------------------------------------------------------------- @@ -410,4 +434,330 @@ public class ResourceService { throw new ServiceException("Error while deleting batch queue: " + e.getMessage(), e); } } + + // ------------------------------------------------------------------------- + // Storage Info + // ------------------------------------------------------------------------- + + public StorageVolumeInfo getResourceStorageInfo(RequestContext ctx, String resourceId, String location) + throws ServiceException { + StorageInfoContext context = resolveStorageInfoContext(ctx, resourceId); + try { + return context.adaptor().getStorageVolumeInfo(location); + } catch (AgentException e) { + throw new ServiceException("Error while retrieving storage volume info for resource " + resourceId + + ": " + e.getMessage(), e); + } + } + + public StorageDirectoryInfo getStorageDirectoryInfo(RequestContext ctx, String resourceId, String location) + throws ServiceException { + StorageInfoContext context = resolveStorageInfoContext(ctx, resourceId); + try { + return context.adaptor().getStorageDirectoryInfo(location); + } catch (AgentException e) { + throw new ServiceException("Error while retrieving storage directory info for resource " + resourceId + + ": " + e.getMessage(), e); + } + } + + /** + * Detects whether resourceId is a compute or storage resource and resolves the appropriate context. + */ + private StorageInfoContext resolveStorageInfoContext(RequestContext ctx, String resourceId) + throws ServiceException { + Optional<ComputeResourceDescription> computeResourceOp = Optional.empty(); + try { + ComputeResourceDescription cr = registryHandler.getComputeResource(resourceId); + if (cr != null) { + computeResourceOp = Optional.of(cr); + } + } catch (TApplicationException e) { + logger.debug("Compute resource {} not found (TApplicationException): {}", resourceId, e.getMessage()); + } catch (Exception e) { + throw new ServiceException("Error looking up compute resource " + resourceId + ": " + e.getMessage(), e); + } + + Optional<StorageResourceDescription> storageResourceOp = Optional.empty(); + if (computeResourceOp.isEmpty()) { + try { + StorageResourceDescription sr = registryHandler.getStorageResource(resourceId); + if (sr != null) { + storageResourceOp = Optional.of(sr); + } + } catch (TApplicationException e) { + logger.debug("Storage resource {} not found (TApplicationException): {}", resourceId, e.getMessage()); + } catch (Exception e) { + throw new ServiceException("Error looking up storage resource " + resourceId + ": " + e.getMessage(), e); + } + } + + if (computeResourceOp.isEmpty() && storageResourceOp.isEmpty()) { + throw new ServiceException("Resource with ID '" + resourceId + + "' not found as either compute resource or storage resource"); + } + + try { + if (computeResourceOp.isPresent()) { + logger.debug("Found compute resource with ID {}. Resolving login username and credentials", resourceId); + return resolveComputeStorageInfoContext(ctx, resourceId); + } else { + logger.debug("Found storage resource with ID {}. Resolving login username and credentials", resourceId); + return resolveStorageStorageInfoContext(ctx, resourceId); + } + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error resolving storage info context for resource " + resourceId + + ": " + e.getMessage(), e); + } + } + + /** + * 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(RequestContext ctx, String resourceId) + throws ServiceException { + String gatewayId = ctx.getGatewayId(); + String userId = ctx.getUserId(); + + String loginUserName = null; + boolean loginFromUserPref = false; + GroupComputeResourcePreference groupComputePref = null; + GroupResourceProfile groupResourceProfile = null; + + UserComputeResourcePreference userComputePref = null; + try { + if (registryHandler.isUserResourceProfileExists(userId, gatewayId)) { + userComputePref = registryHandler.getUserComputeResourcePreference(userId, gatewayId, resourceId); + } else { + logger.debug( + "User resource profile does not exist for user {} in gateway {}, will try group preferences", + userId, gatewayId); + } + } catch (Exception e) { + throw new ServiceException("Error retrieving user compute resource preference: " + e.getMessage(), e); + } + + 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; + try { + groupResourceProfiles = groupResourceProfileService.getGroupResourceList(ctx, gatewayId); + } catch (Exception e) { + throw new ServiceException("Error retrieving group resource profiles: " + e.getMessage(), e); + } + 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) { + throw new ServiceException("No login username found for compute resource " + resourceId); + } + } + + // Resolve credential token based on where login came from + String credentialToken; + if (loginFromUserPref) { + if (userComputePref != null + && userComputePref.getResourceSpecificCredentialStoreToken() != null + && !userComputePref.getResourceSpecificCredentialStoreToken().trim().isEmpty()) { + credentialToken = userComputePref.getResourceSpecificCredentialStoreToken(); + } else { + try { + UserResourceProfile userResourceProfile = + registryHandler.getUserResourceProfile(userId, gatewayId); + if (userResourceProfile == null + || userResourceProfile.getCredentialStoreToken() == null + || userResourceProfile.getCredentialStoreToken().trim().isEmpty()) { + throw new ServiceAuthorizationException( + "No credential store token found for user " + userId + " in gateway " + gatewayId); + } + credentialToken = userResourceProfile.getCredentialStoreToken(); + } catch (ServiceAuthorizationException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error retrieving user resource profile: " + e.getMessage(), e); + } + } + } else { + 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 { + try { + UserResourceProfile userResourceProfile = + registryHandler.getUserResourceProfile(userId, gatewayId); + if (userResourceProfile == null + || userResourceProfile.getCredentialStoreToken() == null + || userResourceProfile.getCredentialStoreToken().trim().isEmpty()) { + throw new ServiceAuthorizationException( + "No credential store token found for compute resource " + resourceId); + } + credentialToken = userResourceProfile.getCredentialStoreToken(); + } catch (ServiceAuthorizationException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error retrieving user resource profile: " + e.getMessage(), e); + } + } + } + + try { + 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); + } catch (AgentException e) { + throw new ServiceException("Error creating SSH adaptor for compute resource " + resourceId + + ": " + e.getMessage(), e); + } + } + + /** + * 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(RequestContext ctx, String resourceId) + throws ServiceException { + String gatewayId = ctx.getGatewayId(); + String userId = ctx.getUserId(); + + UserStoragePreference userStoragePref = null; + try { + if (registryHandler.isUserResourceProfileExists(userId, gatewayId)) { + userStoragePref = registryHandler.getUserStoragePreference(userId, gatewayId, resourceId); + } else { + logger.debug( + "User resource profile does not exist for user {} in gateway {}, will try gateway preferences", + userId, gatewayId); + } + } catch (Exception e) { + throw new ServiceException("Error retrieving user storage preference: " + e.getMessage(), e); + } + + StoragePreference storagePref = null; + try { + GatewayResourceProfile gwProfile = registryHandler.getGatewayResourceProfile(gatewayId); + if (gwProfile != null) { + storagePref = registryHandler.getGatewayStoragePreference(gatewayId, resourceId); + } else { + logger.debug("Gateway resource profile does not exist for gateway {}, will check user preference", + gatewayId); + } + } catch (TApplicationException e) { + logger.debug("Gateway resource profile does not exist for gateway {}: {}", gatewayId, e.getMessage()); + } catch (Exception e) { + throw new ServiceException("Error retrieving gateway storage preference: " + e.getMessage(), e); + } + + 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 { + throw new ServiceException("No login username found for storage resource " + resourceId); + } + + String credentialToken; + if (loginFromUserPref) { + if (userStoragePref != null + && userStoragePref.getResourceSpecificCredentialStoreToken() != null + && !userStoragePref.getResourceSpecificCredentialStoreToken().trim().isEmpty()) { + credentialToken = userStoragePref.getResourceSpecificCredentialStoreToken(); + } else { + try { + UserResourceProfile userResourceProfile = + registryHandler.getUserResourceProfile(userId, gatewayId); + if (userResourceProfile == null + || userResourceProfile.getCredentialStoreToken() == null + || userResourceProfile.getCredentialStoreToken().trim().isEmpty()) { + throw new ServiceAuthorizationException( + "No credential store token found for user " + userId + " in gateway " + gatewayId); + } + credentialToken = userResourceProfile.getCredentialStoreToken(); + } catch (ServiceAuthorizationException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error retrieving user resource profile: " + e.getMessage(), e); + } + } + } else { + if (storagePref != null + && storagePref.getResourceSpecificCredentialStoreToken() != null + && !storagePref.getResourceSpecificCredentialStoreToken().trim().isEmpty()) { + credentialToken = storagePref.getResourceSpecificCredentialStoreToken(); + } else { + try { + GatewayResourceProfile gatewayResourceProfile = + registryHandler.getGatewayResourceProfile(gatewayId); + if (gatewayResourceProfile == null + || gatewayResourceProfile.getCredentialStoreToken() == null + || gatewayResourceProfile.getCredentialStoreToken().trim().isEmpty()) { + throw new ServiceAuthorizationException( + "No credential store token found for gateway " + gatewayId); + } + credentialToken = gatewayResourceProfile.getCredentialStoreToken(); + } catch (ServiceAuthorizationException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error retrieving gateway resource profile: " + e.getMessage(), e); + } + } + } + + try { + 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); + } catch (AgentException e) { + throw new ServiceException("Error creating SSH adaptor for storage resource " + resourceId + + ": " + e.getMessage(), e); + } + } + + /** + * Holds storage info context: login username, credential token, and adaptor. + */ + private record StorageInfoContext(String loginUserName, String credentialToken, AgentAdaptor adaptor) {} } diff --git a/airavata-api/src/test/java/org/apache/airavata/service/resource/ResourceServiceTest.java b/airavata-api/src/test/java/org/apache/airavata/service/resource/ResourceServiceTest.java index 5c97ca7700..a4b1189314 100644 --- a/airavata-api/src/test/java/org/apache/airavata/service/resource/ResourceServiceTest.java +++ b/airavata-api/src/test/java/org/apache/airavata/service/resource/ResourceServiceTest.java @@ -9,6 +9,7 @@ import org.apache.airavata.model.data.movement.SCPDataMovement; import org.apache.airavata.model.data.movement.UnicoreDataMovement; import org.apache.airavata.registry.api.service.handler.RegistryServerHandler; import org.apache.airavata.service.exception.ServiceException; +import org.apache.airavata.service.groupprofile.GroupResourceProfileService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -24,12 +25,13 @@ import static org.mockito.Mockito.*; class ResourceServiceTest { @Mock RegistryServerHandler registryHandler; + @Mock GroupResourceProfileService groupResourceProfileService; ResourceService resourceService; @BeforeEach void setUp() { - resourceService = new ResourceService(registryHandler); + resourceService = new ResourceService(registryHandler, groupResourceProfileService); } // --- Compute Resource ---
