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>'].