blob: da86e071caff743543705cbbac9bd7b0818f62b8 [file] [log] [blame]
<template>
<div v-if="showQueueSettings">
<div class="row">
<div class="col">
<div class="card border-default" :class="{ 'border-danger': !valid }">
<b-link
@click="showConfiguration = !showConfiguration"
class="card-link text-dark"
>
<div class="card-body">
<h5 class="card-title mb-4">
Settings for queue {{ data.queueName }}
</h5>
<div class="row">
<div class="col">
<h3 class="h5 mb-0">
{{ data.nodeCount }}
</h3>
<span class="text-muted text-uppercase">NODE COUNT</span>
</div>
<div class="col">
<h3 class="h5 mb-0">
{{ data.totalCPUCount }}
</h3>
<span class="text-muted text-uppercase">CORE COUNT</span>
</div>
<div class="col">
<h3 class="h5 mb-0">{{ data.wallTimeLimit }} minutes</h3>
<span class="text-muted text-uppercase">TIME LIMIT</span>
</div>
<div class="col" v-if="maxPhysicalMemory > 0">
<h3 class="h5 mb-0">{{ data.totalPhysicalMemory }} MB</h3>
<span class="text-muted text-uppercase">PHYSICAL MEMORY</span>
</div>
</div>
</div>
</b-link>
</div>
</div>
</div>
<div v-if="showConfiguration">
<div class="row">
<div class="col">
<b-form-group
label="Select a Queue"
label-for="queue"
:invalid-feedback="getValidationFeedback('queueName')"
:state="getValidationState('queueName')"
>
<b-form-select
id="queue"
v-model="data.queueName"
:options="queueOptions"
required
@change="queueChanged"
:state="getValidationState('queueName')"
>
</b-form-select>
<div slot="description">
{{ queueDescription }}
</div>
</b-form-group>
<div class="d-flex flex-row">
<div class="flex-fill">
<b-form-group
label="Node Count"
label-for="node-count"
:invalid-feedback="getValidationFeedback('nodeCount')"
:state="getValidationState('nodeCount', true)"
>
<b-form-input
id="node-count"
type="number"
min="1"
:max="maxNodes"
v-model="data.nodeCount"
required
@input="nodeCountChanged"
:state="getValidationState('nodeCount', true)"
>
</b-form-input>
<div slot="description">
<i class="fa fa-info-circle" aria-hidden="true"></i>
Max Allowed Nodes = {{ maxNodes }}
</div>
</b-form-group>
<b-form-group
label="Total Core Count"
label-for="core-count"
:invalid-feedback="getValidationFeedback('totalCPUCount')"
:state="getValidationState('totalCPUCount', true)"
>
<b-form-input
id="core-count"
type="number"
min="1"
:max="maxCPUCount"
v-model="data.totalCPUCount"
required
@input="cpuCountChanged"
:state="getValidationState('totalCPUCount', true)"
>
</b-form-input>
<div slot="description">
<i class="fa fa-info-circle" aria-hidden="true"></i>
Max Allowed Cores = {{ maxCPUCount
}}<template
v-if="
selectedQueueDefault && selectedQueueDefault.cpuPerNode > 0
"
>. There are {{ selectedQueueDefault.cpuPerNode }} cores per
node.
</template>
</div>
</b-form-group>
</div>
<div class="d-flex flex-column" v-if="selectedQueueDefault && selectedQueueDefault.cpuPerNode > 0">
<div class="flex-fill"
style="border: 1px solid #6c757d;border-top-right-radius: 10px;margin-top: 51px;border-left-width: 0px;border-bottom-width: 0px;margin-right: 15px;"></div>
<b-button size="sm" pill variant="outline-secondary"
v-on:click="enableNodeCountToCpuCheck = !enableNodeCountToCpuCheck">
<i v-if="enableNodeCountToCpuCheck" class="fa fa-lock" aria-hidden="true"></i>
<i v-else class="fa fa-unlock" aria-hidden="true"></i>
</b-button>
<div class="flex-fill"
style="border: 1px solid #6c757d;border-bottom-right-radius: 10px;margin-bottom: 57px;border-left-width: 0px;border-top-width: 0px;margin-right: 15px;"></div>
</div>
</div>
<b-form-group
label="Wall Time Limit"
label-for="walltime-limit"
:invalid-feedback="getValidationFeedback('wallTimeLimit')"
:state="getValidationState('wallTimeLimit', true)"
>
<b-input-group append="minutes">
<b-form-input
id="walltime-limit"
type="number"
min="1"
:max="maxWalltime"
v-model="data.wallTimeLimit"
required
:state="getValidationState('wallTimeLimit', true)"
>
</b-form-input>
</b-input-group>
<div slot="description">
<i class="fa fa-info-circle" aria-hidden="true"></i>
Max Allowed Wall Time = {{ maxWalltime }} minutes
</div>
</b-form-group>
<b-form-group
v-if="maxPhysicalMemory > 0"
label="Total Physical Memory"
label-for="total-physical-memory"
:invalid-feedback="getValidationFeedback('totalPhysicalMemory')"
:state="getValidationState('totalPhysicalMemory', true)"
>
<b-input-group append="MB">
<b-form-input
id="total-physical-memory"
type="number"
min="0"
:max="maxPhysicalMemory"
v-model="data.totalPhysicalMemory"
:state="getValidationState('totalPhysicalMemory', true)"
>
</b-form-input>
</b-input-group>
<div slot="description">
<i class="fa fa-info-circle" aria-hidden="true"></i>
Max Physical Memory = {{ maxPhysicalMemory }} MB
</div>
</b-form-group>
<div>
<a
class="text-secondary action-link"
href="#"
@click.prevent="showConfiguration = false"
>
<i class="fa fa-times text-secondary" aria-hidden="true"></i>
Hide Settings</a
>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { models, services } from "django-airavata-api";
import { mixins, utils } from "django-airavata-common-ui";
export default {
name: "queue-settings-editor",
mixins: [mixins.VModelMixin],
props: {
value: {
type: models.ComputationalResourceSchedulingModel,
},
appDeploymentId: {
type: String,
required: true,
},
appModuleId: {
type: String,
required: true,
},
computeResourcePolicy: {
type: models.ComputeResourcePolicy,
required: false,
},
batchQueueResourcePolicies: {
type: Array,
required: false,
},
},
data() {
return {
showConfiguration: false,
appDeploymentQueues: null,
enableNodeCountToCpuCheck: true,
applicationInterface: null,
};
},
computed: {
queueOptions: function () {
const queueOptions = this.queueDefaults.map((queueDefault) => {
return {
value: queueDefault.queueName,
text: queueDefault.queueName,
};
});
return queueOptions;
},
selectedQueueDefault: function () {
return this.queueDefaults.find(
(queue) => queue.queueName === this.data.queueName
);
},
maxCPUCount: function () {
if (!this.selectedQueueDefault) {
return 0;
}
const batchQueueResourcePolicy = this.batchQueueResourcePolicy;
if (batchQueueResourcePolicy) {
return Math.min(
batchQueueResourcePolicy.maxAllowedCores,
this.selectedQueueDefault.maxProcessors
);
}
return this.selectedQueueDefault.maxProcessors;
},
maxNodes: function () {
if (!this.selectedQueueDefault) {
return 0;
}
const batchQueueResourcePolicy = this.batchQueueResourcePolicy;
if (batchQueueResourcePolicy) {
return Math.min(
batchQueueResourcePolicy.maxAllowedNodes,
this.selectedQueueDefault.maxNodes
);
}
return this.selectedQueueDefault.maxNodes;
},
maxWalltime: function () {
if (!this.selectedQueueDefault) {
return 0;
}
const batchQueueResourcePolicy = this.batchQueueResourcePolicy;
if (batchQueueResourcePolicy) {
return Math.min(
batchQueueResourcePolicy.maxAllowedWalltime,
this.selectedQueueDefault.maxRunTime
);
}
return this.selectedQueueDefault.maxRunTime;
},
maxPhysicalMemory: function () {
if (!this.selectedQueueDefault) {
return 0;
}
return this.selectedQueueDefault.maxMemory;
},
queueDefaults() {
return this.appDeploymentQueues
? this.appDeploymentQueues
.filter((q) => this.isQueueInComputeResourcePolicy(q.queueName))
.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);
}
})
: [];
},
defaultQueue() {
if (this.queueDefaults.length === 0) {
return null;
}
return this.queueDefaults[0];
},
batchQueueResourcePolicy() {
if (!this.selectedQueueDefault) {
return null;
}
return this.getBatchQueueResourcePolicy(
this.selectedQueueDefault.queueName
);
},
queueDescription() {
return this.selectedQueueDefault
? this.selectedQueueDefault.queueDescription
: null;
},
validation() {
// Don't run validation if we don't have selectedQueueDefault
if (!this.selectedQueueDefault) {
return this.data.validate();
}
return this.data.validate(
this.selectedQueueDefault,
this.batchQueueResourcePolicy
);
},
valid() {
return Object.keys(this.validation).length === 0;
},
showQueueSettings() {
return this.applicationInterface
? this.applicationInterface.showQueueSettings
: false;
},
},
methods: {
queueChanged: function (queueName) {
const queueDefault = this.queueDefaults.find(
(queue) => queue.queueName === queueName
);
this.data.totalCPUCount = this.getDefaultCPUCount(queueDefault);
this.data.nodeCount = this.getDefaultNodeCount(queueDefault);
this.data.wallTimeLimit = this.getDefaultWalltime(queueDefault);
if (this.maxPhysicalMemory === 0) {
this.data.totalPhysicalMemory = 0;
}
},
validate() {
if (!this.valid) {
this.$emit("invalid");
} else {
this.$emit("valid");
}
},
loadAppDeploymentQueues() {
return services.ApplicationDeploymentService.getQueues({
lookup: this.appDeploymentId,
}).then((queueDefaults) => (this.appDeploymentQueues = queueDefaults));
},
setDefaultQueue() {
if (this.queueDefaults.length === 0) {
this.data.queueName = null;
return;
}
const defaultQueue = this.queueDefaults[0];
this.data.queueName = defaultQueue.queueName;
this.data.totalCPUCount = this.getDefaultCPUCount(defaultQueue);
this.data.nodeCount = this.getDefaultNodeCount(defaultQueue);
this.data.wallTimeLimit = this.getDefaultWalltime(defaultQueue);
if (this.maxPhysicalMemory === 0) {
this.data.totalPhysicalMemory = 0;
}
},
isQueueInComputeResourcePolicy: function (queueName) {
if (!this.computeResourcePolicy) {
return true;
}
return this.computeResourcePolicy.allowedBatchQueues.includes(queueName);
},
getBatchQueueResourcePolicy: function (queueName) {
if (
!this.batchQueueResourcePolicies ||
this.batchQueueResourcePolicies.length === 0
) {
return null;
}
return this.batchQueueResourcePolicies.find(
(bqrp) => bqrp.queuename === queueName
);
},
getDefaultCPUCount: function (queueDefault) {
const batchQueueResourcePolicy = this.batchQueueResourcePolicy;
if (batchQueueResourcePolicy) {
return Math.min(
batchQueueResourcePolicy.maxAllowedCores,
queueDefault.defaultCPUCount
);
}
return queueDefault.defaultCPUCount;
},
getDefaultNodeCount: function (queueDefault) {
const batchQueueResourcePolicy = this.batchQueueResourcePolicy;
if (batchQueueResourcePolicy) {
return Math.min(
batchQueueResourcePolicy.maxAllowedNodes,
queueDefault.defaultNodeCount
);
}
return queueDefault.defaultNodeCount;
},
getDefaultWalltime: function (queueDefault) {
const batchQueueResourcePolicy = this.batchQueueResourcePolicy;
if (batchQueueResourcePolicy) {
return Math.min(
batchQueueResourcePolicy.maxAllowedWalltime,
queueDefault.defaultWalltime
);
}
return queueDefault.defaultWalltime;
},
getValidationFeedback: function (properties) {
return utils.getProperty(this.validation, properties);
},
getValidationState: function (properties, showValidState) {
return this.getValidationFeedback(properties)
? false
: showValidState
? true
: null;
},
applyBatchQueueResourcePolicy() {
// Apply batchQueueResourcePolicy maximums
if (this.selectedQueueDefault) {
this.data.totalCPUCount = Math.min(
this.data.totalCPUCount,
this.maxCPUCount
);
this.data.nodeCount = Math.min(this.data.nodeCount, this.maxNodes);
this.data.wallTimeLimit = Math.min(
this.data.wallTimeLimit,
this.maxWalltime
);
}
},
nodeCountChanged() {
if (this.enableNodeCountToCpuCheck && this.selectedQueueDefault.cpuPerNode > 0) {
const nodeCount = parseInt(this.data.nodeCount);
this.data.totalCPUCount = Math.min(
nodeCount * this.selectedQueueDefault.cpuPerNode,
this.maxCPUCount
);
}
},
cpuCountChanged() {
if (this.enableNodeCountToCpuCheck && this.selectedQueueDefault.cpuPerNode > 0) {
const cpuCount = parseInt(this.data.totalCPUCount);
if (cpuCount > 0) {
this.data.nodeCount = Math.min(
Math.ceil(cpuCount / this.selectedQueueDefault.cpuPerNode),
this.maxNodes
);
}
}
},
loadApplicationInterface() {
services.ApplicationModuleService.getApplicationInterface({
lookup: this.appModuleId,
}).then(
(applicationInterface) =>
(this.applicationInterface = applicationInterface)
);
},
},
watch: {
enableNodeCountToCpuCheck() {
if (this.enableNodeCountToCpuCheck) {
this.nodeCountChanged();
}
},
appDeploymentId() {
this.loadAppDeploymentQueues().then(() => this.setDefaultQueue());
},
// If batch queue policy changes, apply any maximum values to current values
batchQueueResourcePolicy(value, oldValue) {
if (
value &&
(!oldValue || value.resourcePolicyId !== oldValue.resourcePolicyId)
) {
this.applyBatchQueueResourcePolicy();
}
},
computeResourcePolicy() {
if (!this.isQueueInComputeResourcePolicy(this.data.queueName)) {
this.setDefaultQueue();
}
},
},
mounted: function () {
this.loadAppDeploymentQueues().then(() => {
// For brand new queue settings (no queueName specified) load the default
// queue and its default values and apply them
if (!this.value.queueName) {
this.setDefaultQueue();
}
});
this.$on("input", () => this.validate());
this.loadApplicationInterface();
},
};
</script>
<style></style>