This is an automated email from the ASF dual-hosted git repository.
yasithdev pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git
The following commit(s) were added to refs/heads/main by this push:
new 169a10466 feat(portal): repoint experiment + group-resource-profile
writes to gRPC (Track D, D3) (#178)
169a10466 is described below
commit 169a1046640744849168767d095425e55050940e
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Mon Jun 8 21:15:42 2026 -0400
feat(portal): repoint experiment + group-resource-profile writes to gRPC
(Track D, D3) (#178)
Migrate the two deep write families from the Thrift client to the gRPC
facades:
- ExperimentViewSet perform_create/perform_update/launch/cancel ->
research.create_/update_experiment, get_experiment (read-back),
terminate_experiment
- GroupResourceProfileViewSet perform_create/perform_update/perform_destroy
-> compute.create_/update_/remove_group_resource_profile and the three
remove_group_* policy/pref calls (facade arg order verified; the large
OrderedDict->Thrift pre-processing in perform_update is unchanged)
Add grpc_requests reverse adapters: experiment (+ _user_configuration_data
and _computational_resource_scheduling; reuses the input/output reverse
adapters), and group_resource_profile (+ the compute-pref slurm/aws proto
oneof, reservations, SSH configs, both policy types). The GRP protocol
enums reuse the inverse name maps added in the previous batch; experiment_
type goes through the prefix-tolerant _proto_enum.
Verified: manage.py check clean; offline thrift->proto->thrift round-trips
confirm the ExperimentType enum, the userConfiguration/scheduling nesting,
and the GRP slurm-union + divergent JSP_CLOUD enum all survive. (Live
create is backend-blocked for these in the dev env -- experiment needs a
valid app-interface entity, GRP create needs full user context -- the same
limitations noted for their reads.)
Note (reported by the codegen pass): experiment_util.launch/clone live in
the external airavata_django_portal_sdk package and are still Thrift
(incl. the launchExperiment RPC) -- a separate migration.
Reverse adapters drafted by parallel subagents, integrated + validated here.
---
.../django_airavata/apps/api/grpc_requests.py | 213 +++++++++++++++++++++
.../django_airavata/apps/api/views.py | 52 +++--
2 files changed, 236 insertions(+), 29 deletions(-)
diff --git a/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
b/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
index 41b309b98..b5b40806e 100644
--- a/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
+++ b/airavata-django-portal/django_airavata/apps/api/grpc_requests.py
@@ -20,11 +20,17 @@ from airavata.model.appcatalog.computeresource.ttypes
import (
from airavata.model.appcatalog.parallelism.ttypes import (
ApplicationParallelismType as _ThriftParallelismType,
)
+from airavata.model.appcatalog.groupresourceprofile.ttypes import (
+ ResourceType as _ThriftResourceType,
+)
from airavata.model.appcatalog.parser.ttypes import IOType as _ThriftIOType
from airavata.model.application.io.ttypes import DataType as _ThriftDataType
from airavata.model.data.movement.ttypes import (
DataMovementProtocol as _ThriftDataMovementProtocol,
)
+from airavata.model.experiment.ttypes import (
+ ExperimentType as _ThriftExperimentType,
+)
from airavata.model.workspace.ttypes import (
NotificationPriority as _ThriftNotificationPriority,
)
@@ -330,3 +336,210 @@ def gateway_resource_profile(t):
identity_server_tenant=t.identityServerTenant or '',
identity_server_pwd_cred_token=t.identityServerPwdCredToken or '',
)
+
+
+# --- Experiment tree (write direction) -------------------------------------
+# Reverse of grpc_adapters.experiment. The write path carries only what the
+# user submitted; status/errors/processes/workflow are server-managed.
+
+
+def _computational_resource_scheduling(t):
+ """Thrift ``ComputationalResourceSchedulingModel`` -> proto message."""
+ return
_pb2("scheduling.scheduling_pb2").ComputationalResourceSchedulingModel(
+ resource_host_id=t.resourceHostId or '',
+ total_cpu_count=t.totalCPUCount or 0,
+ node_count=t.nodeCount or 0,
+ number_of_threads=t.numberOfThreads or 0,
+ queue_name=t.queueName or '',
+ wall_time_limit=t.wallTimeLimit or 0,
+ total_physical_memory=t.totalPhysicalMemory or 0,
+ chessis_number=t.chessisNumber or '',
+ static_working_dir=t.staticWorkingDir or '',
+ override_login_user_name=t.overrideLoginUserName or '',
+ override_scratch_location=t.overrideScratchLocation or '',
+ override_allocation_project_number=t.overrideAllocationProjectNumber
or '',
+ m_group_count=t.mGroupCount or 0,
+ )
+
+
+def _user_configuration_data(t):
+ """Thrift ``UserConfigurationDataModel`` -> proto message."""
+ ucd = _pb2("experiment.experiment_pb2").UserConfigurationDataModel(
+ airavata_auto_schedule=bool(t.airavataAutoSchedule),
+ override_manual_scheduled_params=bool(t.overrideManualScheduledParams),
+ share_experiment_publicly=bool(t.shareExperimentPublicly),
+ throttle_resources=bool(t.throttleResources),
+ user_dn=t.userDN or '',
+ generate_cert=bool(t.generateCert),
+ input_storage_resource_id=t.inputStorageResourceId or '',
+ output_storage_resource_id=t.outputStorageResourceId or '',
+ experiment_data_dir=t.experimentDataDir or '',
+ use_user_cr_pref=bool(t.useUserCRPref),
+ group_resource_profile_id=t.groupResourceProfileId or '',
+ auto_scheduled_comp_resource_scheduling_list=[
+ _computational_resource_scheduling(s)
+ for s in (t.autoScheduledCompResourceSchedulingList or [])],
+ )
+ if t.computationalResourceScheduling is not None:
+ ucd.computational_resource_scheduling.CopyFrom(
+
_computational_resource_scheduling(t.computationalResourceScheduling))
+ return ucd
+
+
+def experiment(t):
+ """Thrift ``ExperimentModel`` -> proto ``ExperimentModel`` request message.
+
+ Only the user-submitted fields are populated; experiment_status, errors and
+ processes are server-managed (left empty), workflow omitted.
+ """
+ exp_pb = _pb2("experiment.experiment_pb2")
+ e = exp_pb.ExperimentModel(
+ experiment_id=t.experimentId or '',
+ project_id=t.projectId or '',
+ gateway_id=t.gatewayId or '',
+ experiment_type=_proto_enum(
+ exp_pb.ExperimentType, _ThriftExperimentType, t.experimentType,
+ 'EXPERIMENT_TYPE_'),
+ user_name=t.userName or '',
+ experiment_name=t.experimentName or '',
+ description=t.description or '',
+ execution_id=t.executionId or '',
+ enable_email_notification=bool(t.enableEmailNotification),
+ email_addresses=list(t.emailAddresses or []),
+ experiment_inputs=[
+ _input_data_object(i) for i in (t.experimentInputs or [])],
+ experiment_outputs=[
+ _output_data_object(o) for o in (t.experimentOutputs or [])],
+ )
+ if t.userConfigurationData is not None:
+ e.user_configuration_data.CopyFrom(
+ _user_configuration_data(t.userConfigurationData))
+ return e
+
+
+# --- Group resource profile (write direction) ------------------------------
+# Reverse of grpc_adapters.group_resource_profile. Reuses the _proto_enum_rev
+# protocol maps defined above.
+
+
+def _grp_pb2():
+ return _pb2("appcatalog.groupresourceprofile.group_resource_profile_pb2")
+
+
+def _reverse_compute_resource_reservation(t):
+ return _grp_pb2().ComputeResourceReservation(
+ reservation_id=t.reservationId or '',
+ reservation_name=t.reservationName or '',
+ queue_names=list(t.queueNames or []),
+ start_time=t.startTime or 0,
+ end_time=t.endTime or 0,
+ )
+
+
+def _reverse_group_account_ssh_provisioner_config(t):
+ return _grp_pb2().GroupAccountSSHProvisionerConfig(
+ resource_id=t.resourceId or '',
+ group_resource_profile_id=t.groupResourceProfileId or '',
+ config_name=t.configName or '',
+ config_value=t.configValue or '',
+ )
+
+
+def _reverse_slurm_compute_resource_preference(t):
+ return _grp_pb2().SlurmComputeResourcePreference(
+ allocation_project_number=t.allocationProjectNumber or '',
+ preferred_batch_queue=t.preferredBatchQueue or '',
+ quality_of_service=t.qualityOfService or '',
+ usage_reporting_gateway_id=t.usageReportingGatewayId or '',
+ ssh_account_provisioner=t.sshAccountProvisioner or '',
+ group_ssh_account_provisioner_configs=[
+ _reverse_group_account_ssh_provisioner_config(c)
+ for c in (t.groupSSHAccountProvisionerConfigs or [])],
+ ssh_account_provisioner_additional_info=(
+ t.sshAccountProvisionerAdditionalInfo or ''),
+ reservations=[
+ _reverse_compute_resource_reservation(r)
+ for r in (t.reservations or [])],
+ )
+
+
+def _reverse_aws_compute_resource_preference(t):
+ return _grp_pb2().AwsComputeResourcePreference(
+ region=t.region or '',
+ preferred_ami_id=t.preferredAmiId or '',
+ preferred_instance_type=t.preferredInstanceType or '',
+ )
+
+
+def _reverse_group_compute_resource_preference(t):
+ grp = _grp_pb2()
+ cr = _pb2("appcatalog.computeresource.compute_resource_pb2")
+ dm = _pb2("data.movement.data_movement_pb2")
+ msg = grp.GroupComputeResourcePreference(
+ compute_resource_id=t.computeResourceId or '',
+ group_resource_profile_id=t.groupResourceProfileId or '',
+ override_by_airavata=bool(t.overridebyAiravata),
+ login_user_name=t.loginUserName or '',
+ scratch_location=t.scratchLocation or '',
+ preferred_job_submission_protocol=_proto_enum_rev(
+ cr.JobSubmissionProtocol, _JOB_SUBMISSION_PROTOCOL_REV,
+ t.preferredJobSubmissionProtocol),
+ preferred_data_movement_protocol=_proto_enum_rev(
+ dm.DataMovementProtocol, _DATA_MOVEMENT_PROTOCOL_REV,
+ t.preferredDataMovementProtocol),
+ resource_specific_credential_store_token=(
+ t.resourceSpecificCredentialStoreToken or ''),
+ resource_type=_proto_enum(
+ grp.ResourceType, _ThriftResourceType, t.resourceType),
+ )
+ sp = getattr(t, 'specificPreferences', None)
+ if sp is not None:
+ if t.resourceType == _ThriftResourceType.AWS and getattr(sp, 'aws',
None):
+
msg.specific_preferences.CopyFrom(grp.EnvironmentSpecificPreferences(
+ aws=_reverse_aws_compute_resource_preference(sp.aws)))
+ elif getattr(sp, 'slurm', None):
+
msg.specific_preferences.CopyFrom(grp.EnvironmentSpecificPreferences(
+ slurm=_reverse_slurm_compute_resource_preference(sp.slurm)))
+ return msg
+
+
+def _reverse_compute_resource_policy(t):
+ return _grp_pb2().ComputeResourcePolicy(
+ resource_policy_id=t.resourcePolicyId or '',
+ compute_resource_id=t.computeResourceId or '',
+ group_resource_profile_id=t.groupResourceProfileId or '',
+ allowed_batch_queues=list(t.allowedBatchQueues or []),
+ )
+
+
+def _reverse_batch_queue_resource_policy(t):
+ return _grp_pb2().BatchQueueResourcePolicy(
+ resource_policy_id=t.resourcePolicyId or '',
+ compute_resource_id=t.computeResourceId or '',
+ group_resource_profile_id=t.groupResourceProfileId or '',
+ queuename=t.queuename or '',
+ max_allowed_nodes=t.maxAllowedNodes or 0,
+ max_allowed_cores=t.maxAllowedCores or 0,
+ max_allowed_walltime=t.maxAllowedWalltime or 0,
+ )
+
+
+def group_resource_profile(t):
+ """Thrift ``GroupResourceProfile`` -> proto message."""
+ return _grp_pb2().GroupResourceProfile(
+ gateway_id=t.gatewayId or '',
+ group_resource_profile_id=t.groupResourceProfileId or '',
+ group_resource_profile_name=t.groupResourceProfileName or '',
+ compute_preferences=[
+ _reverse_group_compute_resource_preference(p)
+ for p in (t.computePreferences or [])],
+ compute_resource_policies=[
+ _reverse_compute_resource_policy(p)
+ for p in (t.computeResourcePolicies or [])],
+ batch_queue_resource_policies=[
+ _reverse_batch_queue_resource_policy(p)
+ for p in (t.batchQueueResourcePolicies or [])],
+ creation_time=t.creationTime or 0,
+ updated_time=t.updatedTime or 0,
+ default_credential_store_token=t.defaultCredentialStoreToken or '',
+ )
diff --git a/airavata-django-portal/django_airavata/apps/api/views.py
b/airavata-django-portal/django_airavata/apps/api/views.py
index 5eed8df0e..1bd7876fc 100644
--- a/airavata-django-portal/django_airavata/apps/api/views.py
+++ b/airavata-django-portal/django_airavata/apps/api/views.py
@@ -228,8 +228,8 @@ class ExperimentViewSet(mixins.CreateModelMixin,
experiment = serializer.save(
gatewayId=self.gateway_id,
userName=self.username)
- experiment_id = self.request.airavata_client.createExperiment(
- self.authz_token, self.gateway_id, experiment)
+ experiment_id = self.request.airavata.research.create_experiment(
+ self.gateway_id, grpc_requests.experiment(experiment))
self._update_workspace_preferences(
project_id=experiment.projectId,
group_resource_profile_id=experiment.userConfigurationData.groupResourceProfileId,
@@ -240,8 +240,8 @@ class ExperimentViewSet(mixins.CreateModelMixin,
experiment = serializer.save(
gatewayId=self.gateway_id,
userName=self.username)
- self.request.airavata_client.updateExperiment(
- self.authz_token, experiment.experimentId, experiment)
+ self.request.airavata.research.update_experiment(
+ experiment.experimentId, grpc_requests.experiment(experiment))
self._update_workspace_preferences(
project_id=experiment.projectId,
group_resource_profile_id=experiment.userConfigurationData.groupResourceProfileId,
@@ -250,12 +250,12 @@ class ExperimentViewSet(mixins.CreateModelMixin,
@action(methods=['post'], detail=True)
def launch(self, request, experiment_id=None):
try:
- experiment = request.airavata_client.getExperiment(
- self.authz_token, experiment_id)
+ experiment = grpc_adapters.experiment(
+ request.airavata.research.get_experiment(experiment_id))
if (experiment.enableEmailNotification):
experiment.emailAddresses = [request.user.email]
- request.airavata_client.updateExperiment(
- self.authz_token, experiment_id, experiment)
+ request.airavata.research.update_experiment(
+ experiment_id, grpc_requests.experiment(experiment))
experiment_util.launch(request, experiment_id)
return Response({'success': True})
except Exception as e:
@@ -284,8 +284,8 @@ class ExperimentViewSet(mixins.CreateModelMixin,
@action(methods=['post'], detail=True)
def cancel(self, request, experiment_id=None):
try:
- request.airavata_client.terminateExperiment(
- request.authz_token, experiment_id, self.gateway_id)
+ request.airavata.research.terminate_experiment(
+ experiment_id, self.gateway_id)
return Response({'success': True})
except Exception as e:
log.exception("Cancel action has thrown the following error",
extra={'request': request})
@@ -972,13 +972,10 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
def perform_create(self, serializer):
group_resource_profile = serializer.save()
group_resource_profile.gatewayId = self.gateway_id
- group_resource_profile_id =
self.request.airavata_client.createGroupResourceProfile(
- authzToken=self.authz_token,
groupResourceProfile=group_resource_profile)
- group_resource_profile.groupResourceProfileId =
group_resource_profile_id
- # Update the creationTime field on the group resource profile
- new_group_resource_profile =
self.request.airavata_client.getGroupResourceProfile(
- self.authz_token, group_resource_profile_id)
- group_resource_profile.creationTime =
new_group_resource_profile.creationTime
+ created = self.request.airavata.compute.create_group_resource_profile(
+ grpc_requests.group_resource_profile(group_resource_profile))
+ group_resource_profile.groupResourceProfileId =
created.group_resource_profile_id
+ group_resource_profile.creationTime = created.creation_time
def perform_update(self, serializer):
original_instance = serializer.instance
@@ -986,19 +983,16 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
grp = serializer.save()
for removed_compute_resource_preference \
in grp._removed_compute_resource_preferences:
- self.request.airavata_client.removeGroupComputePrefs(
- self.authz_token,
- removed_compute_resource_preference.computeResourceId,
- removed_compute_resource_preference.groupResourceProfileId)
+ self.request.airavata.compute.remove_group_compute_prefs(
+ removed_compute_resource_preference.groupResourceProfileId,
+ removed_compute_resource_preference.computeResourceId)
for removed_compute_resource_policy \
in grp._removed_compute_resource_policies:
- self.request.airavata_client.removeGroupComputeResourcePolicy(
- self.authz_token,
+ self.request.airavata.compute.remove_group_compute_resource_policy(
removed_compute_resource_policy.resourcePolicyId)
for removed_batch_queue_resource_policy \
in grp._removed_batch_queue_resource_policies:
- self.request.airavata_client.removeGroupBatchQueueResourcePolicy(
- self.authz_token,
+
self.request.airavata.compute.remove_group_batch_queue_resource_policy(
removed_batch_queue_resource_policy.resourcePolicyId)
if hasattr(grp, 'computePreferences') and grp.computePreferences:
from collections import OrderedDict
@@ -1150,12 +1144,12 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
from
airavata.model.appcatalog.groupresourceprofile.ttypes import
GroupAccountSSHProvisionerConfig
pref.specificPreferences.slurm.groupSSHAccountProvisionerConfigs[cfg_idx] =
GroupAccountSSHProvisionerConfig(**cfg)
- self.request.airavata_client.updateGroupResourceProfile(
- self.authz_token, grp)
+ self.request.airavata.compute.update_group_resource_profile(
+ grp.groupResourceProfileId,
grpc_requests.group_resource_profile(grp))
def perform_destroy(self, instance):
- self.request.airavata_client.removeGroupResourceProfile(
- self.authz_token, instance.groupResourceProfileId)
+ self.request.airavata.compute.remove_group_resource_profile(
+ instance.groupResourceProfileId)
class SharedEntityViewSet(mixins.RetrieveModelMixin,