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
commit ff4151f413a8d72923962accae651ca025561b01 Author: Marcus Christie <[email protected]> AuthorDate: Fri Sep 7 15:59:23 2018 -0400 AIRAVATA-2876 WIP ApplicationDeploymentEditor --- .../applications/ApplicationDeploymentEditor.vue | 100 ++++++++++++++++++++- .../applications/ApplicationModuleEditor.vue | 24 ++--- django_airavata/apps/api/serializers.py | 39 ++++---- .../api/static/django_airavata_api/js/index.js | 7 +- .../js/models/ApplicationDeploymentDescription.js | 46 ++++++++-- .../django_airavata_api/js/models/CommandObject.js | 20 +++++ .../js/models/ParallelismType.js | 12 +++ .../django_airavata_api/js/models/SetEnvPath.js | 21 +++++ .../django_airavata_api/js/service_config.js | 2 +- django_airavata/apps/api/views.py | 21 ++++- 10 files changed, 236 insertions(+), 56 deletions(-) diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentEditor.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentEditor.vue index c619990..b778577 100644 --- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentEditor.vue +++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationDeploymentEditor.vue @@ -1,11 +1,57 @@ <template> - <div>Application Deployment Editor</div> + <div> + <div class="row"> + <div class="col"> + <h1 class="h4 mb-4"> + {{ name }} + </h1> + <b-form-group label="Application Executable Path" label-for="executable-path"> + <b-form-input id="executable-path" type="text" v-model="data.executablePath" required></b-form-input> + </b-form-group> + <b-form-group label="Application Parallelism Type" label-for="parallelism-type"> + <b-form-select id="parallelism-type" v-model="data.parallelism" :options="parallelismTypeOptions" /> + </b-form-group> + <b-form-group label="Application Deployment Description" label-for="deployment-description"> + <b-form-textarea id="deployment-description" v-model="data.appDeploymentDescription" :rows="3"></b-form-textarea> + </b-form-group> + <b-card title="Module Load Commands"> + <b-input-group v-for="moduleLoadCmd in data.moduleLoadCmds" :key="moduleLoadCmd.key" class="mb-1"> + <b-form-input type="text" v-model="moduleLoadCmd.command" required ref="moduleLoadCmdInputs" /> + <b-input-group-append> + <b-button variant="secondary" @click="deleteModuleLoadCmd(moduleLoadCmd)"> + <i class="fa fa-trash"></i> + <span class="sr-only">Delete</span> + </b-button> + </b-input-group-append> + </b-input-group> + <b-button variant="secondary" @click="addModuleLoadCmd">Add Module Load Command</b-button> + </b-card> + </div> + </div> + <div class="row mb-4"> + <div class="col"> + <b-button variant="primary" @click="save"> + Save + </b-button> + <b-button variant="secondary" @click="cancel"> + Cancel + </b-button> + </div> + </div> + </div> </template> <script> +import { models, services } from "django-airavata-api"; +import vmodel_mixin from "../commons/vmodel_mixin"; + export default { name: "application-deployment-editor", + mixins: [vmodel_mixin], props: { + value: { + type: models.ApplicationDescriptionDefinition + }, id: { type: String, required: true @@ -14,6 +60,58 @@ export default { type: String, required: true } + }, + data() { + return { + computeResource: null + }; + }, + computed: { + name() { + if (this.computeResource) { + return this.computeResource.hostName; + } else { + return this.data.computeHostId.substring(0, 10); + } + }, + parallelismTypeOptions() { + return models.ParallelismType.values.map(parType => { + return { + value: parType, + text: parType.name + }; + }); + } + }, + created() { + services.ComputeResourceService.retrieve({ + lookup: this.data.computeHostId + }).then(computeResource => (this.computeResource = computeResource)); + }, + methods: { + save() { + this.$emit("save"); + }, + cancel() { + this.$emit("cancel"); + }, + addModuleLoadCmd() { + if (!this.data.moduleLoadCmds) { + this.data.moduleLoadCmds = []; + } + this.data.moduleLoadCmds.push(new models.CommandObject()); + this.$nextTick(() => + this.$refs.moduleLoadCmdInputs[ + this.$refs.moduleLoadCmdInputs.length - 1 + ].focus() + ); + }, + deleteModuleLoadCmd(moduleLoadCmd) { + const index = this.data.moduleLoadCmds.findIndex( + cmd => cmd.key === moduleLoadCmd.key + ); + this.data.moduleLoadCmds.splice(index, 1); + } } }; </script> diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue index 092fdc9..b6d08a7 100644 --- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue +++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue @@ -6,13 +6,13 @@ Application Details </h1> <b-form-group label="Application Name" label-for="application-name"> - <b-form-input id="application-name" type="text" v-model="appModule.appModuleName" required @input="emitChanged"></b-form-input> + <b-form-input id="application-name" type="text" v-model="data.appModuleName" required></b-form-input> </b-form-group> <b-form-group label="Application Version" label-for="application-version"> - <b-form-input id="application-version" type="text" v-model="appModule.appModuleVersion" @input="emitChanged"></b-form-input> + <b-form-input id="application-version" type="text" v-model="data.appModuleVersion"></b-form-input> </b-form-group> <b-form-group label="Application Description" label-for="application-description"> - <b-form-textarea id="application-description" v-model="appModule.appModuleDescription" :rows="3" @input="emitChanged"></b-form-textarea> + <b-form-textarea id="application-description" v-model="data.appModuleDescription" :rows="3"></b-form-textarea> </b-form-group> </div> </div> @@ -31,35 +31,23 @@ <script> import { models } from "django-airavata-api"; +import vmodel_mixin from "../commons/vmodel_mixin"; export default { name: "application-module-editor", + mixins: [vmodel_mixin], props: { value: { - type: models.ApplicationModule, - required: true + type: models.ApplicationModule } }, - data: function() { - return { - appModule: this.value.clone() - }; - }, methods: { - emitChanged() { - this.$emit("input", this.appModule); - }, save() { this.$emit("save"); }, cancel() { this.$emit("cancel"); } - }, - watch: { - value: function(newValue) { - this.appModule = newValue.clone(); - } } }; </script> diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py index cefabd9..9f91207 100644 --- a/django_airavata/apps/api/serializers.py +++ b/django_airavata/apps/api/serializers.py @@ -313,29 +313,6 @@ class ApplicationInterfaceDescriptionSerializer( return validated_data -class CommandObjectSerializer(CustomSerializer): - command = serializers.CharField() - commandOrder = serializers.IntegerField() - - def create(self, validated_data): - return CommandObject(**validated_data) - - def update(self, instance, validated_data): - raise Exception("Not implemented") - - -class SetEnvPathsSerializer(CustomSerializer): - name=serializers.CharField(required=False) - value=serializers.CharField(required=False) - envPathOrder=serializers.IntegerField(required=False) - - def create(self, validated_data): - return SetEnvPaths(**validated_data) - - def update(self, instance, validated_data): - raise Exception("Not implemented") - - class ApplicationDeploymentDescriptionSerializer( thrift_utils.create_serializer_class( ApplicationDeploymentDescription)): @@ -357,6 +334,22 @@ class ApplicationDeploymentDescriptionSerializer( request.authz_token, appDeployment.appDeploymentId, ResourcePermissionType.WRITE) + def to_representation(self, instance): + rep = super().to_representation(instance) + if rep['moduleLoadCmds'] is not None: + rep['moduleLoadCmds'].sort( + key=lambda cmd: cmd['commandOrder']) + return rep + + def to_internal_value(self, data): + validated_data = super().to_internal_value(data) + # Update application input order based on order in array + module_load_cmds = validated_data.get('moduleLoadCmds', []) + if module_load_cmds is not None: + for i in range(len(module_load_cmds)): + module_load_cmds[i]['commandOrder'] = i + return validated_data + class ComputeResourceDescriptionSerializer(thrift_utils.create_serializer_class(ComputeResourceDescription)): pass 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 6546388..8c9a497 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 @@ -7,6 +7,7 @@ import ApplicationModule from './models/ApplicationModule' import BaseModel from './models/BaseModel' import BatchQueue from './models/BatchQueue' import BatchQueueResourcePolicy from './models/BatchQueueResourcePolicy' +import CommandObject from './models/CommandObject' import ComputeResourcePolicy from './models/ComputeResourcePolicy' import DataType from './models/DataType' import Experiment from './models/Experiment' @@ -18,13 +19,14 @@ import GroupPermission from './models/GroupPermission' import GroupResourceProfile from './models/GroupResourceProfile' import InputDataObjectType from './models/InputDataObjectType' import OutputDataObjectType from './models/OutputDataObjectType' +import ParallelismType from './models/ParallelismType' import Project from './models/Project' import ResourcePermissionType from './models/ResourcePermissionType' +import SetEnvPath from './models/SetEnvPath' import SharedEntity from './models/SharedEntity' import SummaryType from './models/SummaryType' import UserPermission from './models/UserPermission' -import ApplicationDeploymentService from './services/ApplicationDeploymentService' import ExperimentService from './services/ExperimentService' import ExperimentSearchService from './services/ExperimentSearchService' import FullExperimentService from './services/FullExperimentService' @@ -57,6 +59,7 @@ exports.models = { BaseModel, BatchQueue, BatchQueueResourcePolicy, + CommandObject, ComputeResourcePolicy, DataType, Experiment, @@ -68,8 +71,10 @@ exports.models = { GroupResourceProfile, InputDataObjectType, OutputDataObjectType, + ParallelismType, Project, ResourcePermissionType, + SetEnvPath, SharedEntity, SummaryType, UserPermission, 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 index f983455..812d540 100644 --- 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 @@ -1,4 +1,7 @@ import BaseModel from './BaseModel' +import ParallelismType from './ParallelismType'; +import CommandObject from './CommandObject'; +import SetEnvPath from './SetEnvPath'; const FIELDS = [ @@ -6,15 +9,42 @@ const FIELDS = [ 'appModuleId', 'computeHostId', 'executablePath', - 'parallelism', + { + name: 'parallelism', + type: ParallelismType, + default: ParallelismType.SERIAL, + }, 'appDeploymentDescription', - // TODO: map these - // 'moduleLoadCmds', - // 'libPrependPaths', - // 'libAppendPaths', - // 'setEnvironment', - // 'preJobCommands', - // 'postJobCommands', + { + name: 'moduleLoadCmds', + type: CommandObject, + list: true, + }, + { + name: 'libPrependPaths', + type: SetEnvPath, + list: true, + }, + { + name: 'libAppendPaths', + type: SetEnvPath, + list: true, + }, + { + name: 'setEnvironment', + type: SetEnvPath, + list: true, + }, + { + name: 'preJobCommands', + type: CommandObject, + list: true, + }, + { + name: 'postJobCommands', + type: CommandObject, + list: true, + }, 'defaultQueueName', 'defaultNodeCount', 'defaultCPUCount', diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/CommandObject.js b/django_airavata/apps/api/static/django_airavata_api/js/models/CommandObject.js new file mode 100644 index 0000000..60347b0 --- /dev/null +++ b/django_airavata/apps/api/static/django_airavata_api/js/models/CommandObject.js @@ -0,0 +1,20 @@ +import BaseModel from './BaseModel' +import uuidv4 from 'uuid/v4' + + +const FIELDS = [ + 'command', + 'commandOrder', +]; + +export default class CommandObject extends BaseModel { + + constructor(data = {}) { + super(FIELDS, data); + this._key = data.key ? data.key : uuidv4(); + } + + get key() { + return this._key; + } +} diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/ParallelismType.js b/django_airavata/apps/api/static/django_airavata_api/js/models/ParallelismType.js new file mode 100644 index 0000000..c7a6043 --- /dev/null +++ b/django_airavata/apps/api/static/django_airavata_api/js/models/ParallelismType.js @@ -0,0 +1,12 @@ +import BaseEnum from './BaseEnum' + +export default class ParallelismType extends BaseEnum { +} +ParallelismType.init([ + "SERIAL", + "MPI", + "OPENMP", + "OPENMP_MPI", + "CCM", + "CRAY_MPI" +]); diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/SetEnvPath.js b/django_airavata/apps/api/static/django_airavata_api/js/models/SetEnvPath.js new file mode 100644 index 0000000..88165c7 --- /dev/null +++ b/django_airavata/apps/api/static/django_airavata_api/js/models/SetEnvPath.js @@ -0,0 +1,21 @@ +import BaseModel from './BaseModel' +import uuidv4 from 'uuid/v4' + + +const FIELDS = [ + 'name', + 'value', + 'envPathOrder', +]; + +export default class SetEnvPath extends BaseModel { + + constructor(data = {}) { + super(FIELDS, data); + this._key = data.key ? data.key : uuidv4(); + } + + get key() { + return this._key; + } +} diff --git a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js index a737ea2..f7b7408 100644 --- a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js +++ b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js @@ -95,7 +95,7 @@ export default { "ComputeResources": { url: "/api/compute-resources", viewSet: [{ - name: "list" + name: "retrieve" }, { name: "names", url: "/api/compute-resources/all_names/", diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py index 63966bc..3ca84de 100644 --- a/django_airavata/apps/api/views.py +++ b/django_airavata/apps/api/views.py @@ -4,6 +4,7 @@ from django.conf import settings from django.contrib.auth.decorators import login_required from django.core.exceptions import ObjectDoesNotExist from django.http import FileResponse, Http404, JsonResponse +from django.urls import reverse from rest_framework import mixins from rest_framework import status from rest_framework.decorators import detail_route @@ -521,18 +522,30 @@ class ComputeResourceViewSet(mixins.RetrieveModelMixin, lookup_value_regex = '[^/]+' def get_instance(self, lookup_value, format=None): - return self.request.airavata_client.getComputeResource(self.authz_token, lookup_value) + return self.request.airavata_client.getComputeResource( + self.authz_token, lookup_value) @list_route() def all_names(self, request, format=None): """Return a map of compute resource names keyed by resource id.""" - return Response(request.airavata_client.getAllComputeResourceNames(request.authz_token)) + return Response( + request.airavata_client.getAllComputeResourceNames( + request.authz_token)) @list_route() def all_names_list(self, request, format=None): """Return a list of compute resource names keyed by resource id.""" - all_names = request.airavata_client.getAllComputeResourceNames(request.authz_token) - return Response([{'host_id': host_id, 'host': host} for host_id, host in all_names.items()]) + all_names = request.airavata_client.getAllComputeResourceNames( + request.authz_token) + return Response([ + { + 'host_id': host_id, + 'host': host, + 'url': request.build_absolute_uri( + reverse('django_airavata_api:compute-resource-detail', + args=[host_id])) + } for host_id, host in all_names.items() + ]) @detail_route() def queues(self, request, compute_resource_id, format=None):
