You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2017/12/21 17:28:38 UTC
[airavata-django-portal] branch master updated: AIRAVATA-2598
Loading queue defaults for selected app deployment
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
The following commit(s) were added to refs/heads/master by this push:
new 71968d9 AIRAVATA-2598 Loading queue defaults for selected app deployment
71968d9 is described below
commit 71968d96d9d0ddefcc322784bf3f485f97c2139c
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Thu Dec 21 12:28:31 2017 -0500
AIRAVATA-2598 Loading queue defaults for selected app deployment
---
django_airavata/apps/api/serializers.py | 3 +
.../api/static/django_airavata_api/js/index.js | 2 +
.../js/models/ApplicationDeploymentDescription.js | 30 +++++++
.../django_airavata_api/js/models/BatchQueue.js | 23 ++++++
.../js/services/ApplicationDeploymentService.js | 37 +++++++++
.../js/services/ApplicationModuleService.js | 8 +-
django_airavata/apps/api/views.py | 7 ++
.../js/views/CreateExperimentContainer.vue | 2 +-
.../js/views/ExperimentEditor.vue | 95 +++++++++++++++++++++-
django_airavata/static/common/scss/main.scss | 3 +
10 files changed, 204 insertions(+), 6 deletions(-)
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index 77d27a8..28b0124 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -121,10 +121,13 @@ class ExperimentSerializer(serializers.Serializer):
class ApplicationModuleSerializer(serializers.Serializer):
+ url = FullyEncodedHyperlinkedIdentityField(view_name='django_airavata_api:application-detail', lookup_field='appModuleId', lookup_url_kwarg='app_module_id')
appModuleId = serializers.CharField(required=True)
appModuleName = serializers.CharField(required=True)
appModuleDescription = serializers.CharField()
appModuleVersion = serializers.CharField()
+ applicationInterface = FullyEncodedHyperlinkedIdentityField(view_name='django_airavata_api:application-application-interface', lookup_field='appModuleId', lookup_url_kwarg='app_module_id')
+ applicationDeployments = FullyEncodedHyperlinkedIdentityField(view_name='django_airavata_api:application-application-deployments', lookup_field='appModuleId', lookup_url_kwarg='app_module_id')
def create(self, validated_data):
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js
index 2e74456..fc3b6bc 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -6,6 +6,7 @@ import InputDataObjectType from './models/InputDataObjectType'
import OutputDataTypeObject from './models/OutputDataTypeObject'
import Project from './models/Project'
+import ApplicationDeploymentService from './services/ApplicationDeploymentService'
import ApplicationInterfaceService from './services/ApplicationInterfaceService'
import ApplicationModuleService from './services/ApplicationModuleService'
import ProjectService from './services/ProjectService'
@@ -23,6 +24,7 @@ exports.models = {
}
exports.services = {
+ ApplicationDeploymentService,
ApplicationInterfaceService,
ApplicationModuleService,
ProjectService,
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationDeploymentDescription.js b/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationDeploymentDescription.js
new file mode 100644
index 0000000..881575f
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationDeploymentDescription.js
@@ -0,0 +1,30 @@
+import BaseModel from './BaseModel'
+
+
+const FIELDS = [
+ 'appDeploymentId',
+ 'appModuleId',
+ 'computeHostId',
+ 'executablePath',
+ 'parallelism',
+ 'appDeploymentDescription',
+ // TODO: map these
+ // 'moduleLoadCmds',
+ // 'libPrependPaths',
+ // 'libAppendPaths',
+ // 'setEnvironment',
+ // 'preJobCommands',
+ // 'postJobCommands',
+ 'defaultQueueName',
+ 'defaultNodeCount',
+ 'defaultCPUCount',
+ 'defaultWalltime',
+ 'editableByUser',
+];
+
+export default class ApplicationDeploymentDescription extends BaseModel {
+
+ constructor(data = {}) {
+ super(FIELDS, data);
+ }
+}
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueue.js b/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueue.js
new file mode 100644
index 0000000..888b271
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/BatchQueue.js
@@ -0,0 +1,23 @@
+import BaseModel from './BaseModel';
+
+const FIELDS = [
+ 'queueName',
+ 'queueDescription',
+ 'maxRunTime',
+ 'maxNodes',
+ 'maxProcessors',
+ 'maxJobsInQueue',
+ 'maxMemory',
+ 'cpuPerNode',
+ 'defaultNodeCount',
+ 'defaultCPUCount',
+ 'defaultWalltime',
+ 'queueSpecificMacros',
+ 'isDefaultQueue',
+];
+
+export default class BatchQueue extends BaseModel {
+ constructor(data = {}) {
+ super(FIELDS, data);
+ }
+}
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/services/ApplicationDeploymentService.js b/django_airavata/apps/api/static/django_airavata_api/js/services/ApplicationDeploymentService.js
new file mode 100644
index 0000000..ea43477
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/services/ApplicationDeploymentService.js
@@ -0,0 +1,37 @@
+
+import ApplicationDeploymentDescription from '../models/ApplicationDeploymentDescription'
+import BatchQueue from '../models/BatchQueue'
+
+import FetchUtils from '../utils/FetchUtils'
+
+class ApplicationDeploymentService {
+ list(data = null) {
+ if (data) {
+ return Promise.resolve(data.map(result => new ApplicationDeploymentDescription(result)));
+ } else {
+ return FetchUtils.get('/api/application-deployments/')
+ .then(json => json.map(result => new ApplicationDeploymentDescription(result)));
+ }
+ }
+
+ create(project) {
+ // TODO
+ }
+
+ update() {
+ // TODO
+ }
+
+ get(appDeploymentId) {
+ return FetchUtils.get('/api/application-deployments/' + encodeURIComponent(appDeploymentId) + '/')
+ .then(json => new ApplicationDeploymentDescription(json))
+ }
+
+ getQueues(appDeploymentId) {
+ return FetchUtils.get('/api/application-deployments/' + encodeURIComponent(appDeploymentId) + '/queues/')
+ .then(json => json.map(result => new BatchQueue(result)));
+ }
+}
+
+// Export as a singleton
+export default new ApplicationDeploymentService();
\ No newline at end of file
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/services/ApplicationModuleService.js b/django_airavata/apps/api/static/django_airavata_api/js/services/ApplicationModuleService.js
index a3ab350..d11b506 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/services/ApplicationModuleService.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/services/ApplicationModuleService.js
@@ -1,4 +1,5 @@
+import ApplicationDeploymentDescription from '../models/ApplicationDeploymentDescription'
import ApplicationModule from '../models/ApplicationModule'
import FetchUtils from '../utils/FetchUtils'
@@ -22,7 +23,12 @@ class ApplicationModuleService {
get(appModuleId) {
return FetchUtils.get('/api/applications/' + encodeURIComponent(appModuleId) + '/')
- .then(json => new ApplicationModule(json))
+ .then(json => new ApplicationModule(json));
+ }
+
+ getApplicationDeployments(appModuleId) {
+ return FetchUtils.get('/api/applications/' + encodeURIComponent(appModuleId) + '/application_deployments/')
+ .then(json => json.map(result => new ApplicationDeploymentDescription(result)));
}
}
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index 3c832c0..9751a1b 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -276,6 +276,13 @@ class ApplicationModuleViewSet(APIBackedViewSet):
return Response({'error': 'No application interface found for module id {}'.format(app_module_id)},
status=status.HTTP_404_NOT_FOUND)
+ @detail_route()
+ def application_deployments(self, request, app_module_id):
+ all_deployments = self.request.airavata_client.getAllApplicationDeployments(self.authz_token, self.gateway_id)
+ app_deployments = [dep for dep in all_deployments if dep.appModuleId == app_module_id]
+ serializer = serializers.ApplicationDeploymentDescriptionSerializer(app_deployments, many=True, context={'request': request})
+ return Response(serializer.data)
+
# TODO convert to APIBackedViewSet
class RegisterApplicationModule(APIView):
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/views/CreateExperimentContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/views/CreateExperimentContainer.vue
index 90a097d..e440313 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/views/CreateExperimentContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/views/CreateExperimentContainer.vue
@@ -1,5 +1,5 @@
<template>
- <experiment-editor :experiment="experiment" :app-module="appModule" :app-interface="appInterface">
+ <experiment-editor v-if="appModule && appInterface" :experiment="experiment" :app-module="appModule" :app-interface="appInterface">
<span slot="title">Create a New Experiment</span>
</experiment-editor>
</template>
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/views/ExperimentEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/views/ExperimentEditor.vue
index 2d94d2b..6a66601 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/views/ExperimentEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/views/ExperimentEditor.vue
@@ -58,12 +58,42 @@
<b-form novalidate>
<b-form-group label="Compute Resource" label-for="compute-resource">
<b-form-select id="compute-resource"
- v-model="experiment.userConfigurationData.computationalResourceScheduling.resourceHostId" :options="computeResourceOptions" required>
+ v-model="experiment.userConfigurationData.computationalResourceScheduling.resourceHostId"
+ :options="computeResourceOptions" required
+ @change="computeResourceChanged">
<template slot="first">
<option :value="null" disabled>Select a Compute Resource</option>
</template>
</b-form-select>
</b-form-group>
+
+ <div class="card border-default">
+ <div class="card-body">
+ <h4 class="card-title">Current Settings</h4>
+ </div>
+ </div>
+ <b-form-group label="Select a Queue" label-for="queue">
+ <b-form-select id="queue"
+ v-model="experiment.userConfigurationData.computationalResourceScheduling.queueName"
+ :options="queueOptions" required
+ @change="queueChanged">
+ </b-form-select>
+ </b-form-group>
+ <b-form-group label="Node Count" label-for="node-count">
+ <b-form-input id="node-count" type="number" min="1"
+ v-model="experiment.userConfigurationData.computationalResourceScheduling.nodeCount" required>
+ </b-form-input>
+ </b-form-group>
+ <b-form-group label="Total Core Count" label-for="core-count">
+ <b-form-input id="core-count" type="number" min="1"
+ v-model="experiment.userConfigurationData.computationalResourceScheduling.totalCPUCount" required>
+ </b-form-input>
+ </b-form-group>
+ <b-form-group label="Wall Time Limit" label-for="walltime-limit">
+ <b-form-input id="walltime-limit" type="number" min="1"
+ v-model="experiment.userConfigurationData.computationalResourceScheduling.wallTimeLimit" required>
+ </b-form-input>
+ </b-form-group>
</b-form>
</div>
</div>
@@ -80,13 +110,21 @@ export default {
props: ['experiment', 'appModule', 'appInterface'],
data () {
return {
- 'projects': [],
- 'computeResources': {},
+ projects: [],
+ computeResources: {},
+ applicationDeployments: [],
+ queueDefaults: [],
}
},
mounted: function () {
services.ProjectService.listAll()
.then(projects => this.projects = projects);
+ services.ApplicationModuleService.getApplicationDeployments(this.appModule.appModuleId)
+ .then(applicationDeployments => {
+ this.applicationDeployments = applicationDeployments;
+ });
+ services.ApplicationInterfaceService.getComputeResources(this.appInterface.applicationInterfaceId)
+ .then(computeResources => this.computeResources = computeResources);
},
computed: {
projectOptions: function() {
@@ -107,6 +145,55 @@ export default {
}
computeResourceOptions.sort((a, b) => a.text.localeCompare(b.text));
return computeResourceOptions;
+ },
+ queueOptions: function() {
+ const queueOptions = this.queueDefaults.map(queueDefault => {
+ return {
+ value: queueDefault.queueName,
+ text: `${queueDefault.queueName}: ${queueDefault.queueDescription}`,
+ }
+ });
+ return queueOptions;
+ },
+ },
+ methods: {
+ computeResourceChanged: function(selectedComputeResourceId) {
+ // Find application deployment that corresponds to this compute resource
+ // TODO: switch to find()
+ let selectedApplicationDeployments = this.applicationDeployments.filter(dep => dep.computeHostId === selectedComputeResourceId);
+ if (selectedApplicationDeployments.length === 0) {
+ throw new Error("Failed to find application deployment!");
+ }
+ let selectedApplicationDeployment = selectedApplicationDeployments[0];
+ services.ApplicationDeploymentService.getQueues(selectedApplicationDeployment.appDeploymentId)
+ .then(queueDefaults => {
+ // Sort queue defaults
+ this.queueDefaults = queueDefaults.sort((a, b) => {
+ // Sort default first, then by alphabetically by name
+ if (a.isDefaultQueue) {
+ return -1;
+ } else if (b.isDefaultQueue) {
+ return 1;
+ } else {
+ return a.queueName.localeCompare(b.queueName);
+ }
+ });
+ // Find the default queue and apply it's settings
+ const defaultQueue = this.queueDefaults[0];
+
+ // TODO: we really shouldn't be modifying the props right?
+ this.experiment.userConfigurationData.computationalResourceScheduling.queueName = defaultQueue.queueName;
+ this.experiment.userConfigurationData.computationalResourceScheduling.totalCPUCount = defaultQueue.defaultCPUCount;
+ this.experiment.userConfigurationData.computationalResourceScheduling.nodeCount = defaultQueue.defaultNodeCount;
+ this.experiment.userConfigurationData.computationalResourceScheduling.wallTimeLimit = defaultQueue.defaultWalltime;
+ });
+ },
+ queueChanged: function(queueName) {
+
+ const queueDefault = this.queueDefaults.find(queue => queue.queueName === queueName);
+ this.experiment.userConfigurationData.computationalResourceScheduling.totalCPUCount = queueDefault.defaultCPUCount;
+ this.experiment.userConfigurationData.computationalResourceScheduling.nodeCount = queueDefault.defaultNodeCount;
+ this.experiment.userConfigurationData.computationalResourceScheduling.wallTimeLimit = queueDefault.defaultWalltime;
}
},
watch: {
@@ -115,7 +202,7 @@ export default {
services.ApplicationInterfaceService.getComputeResources(this.appInterface.applicationInterfaceId)
.then(computeResources => this.computeResources = computeResources);
}
- }
+ },
}
}
</script>
diff --git a/django_airavata/static/common/scss/main.scss b/django_airavata/static/common/scss/main.scss
index 46167fe..e0a254d 100644
--- a/django_airavata/static/common/scss/main.scss
+++ b/django_airavata/static/common/scss/main.scss
@@ -140,6 +140,9 @@ body {
box-shadow: 0 2px 0 #eee;
margin-bottom: 30px;
}
+.card.border-default {
+ border: 1px solid rgba(0,0,0,.125)
+}
/**
* Make a small card text modifier so we can still use
--
To stop receiving notification emails like this one, please contact
['"commits@airavata.apache.org" <co...@airavata.apache.org>'].