AIRAVATA-3543 Auto update nodeCount/totalCPUCount based on cpuPerNode
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/QueueSettingsEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/QueueSettingsEditor.vue
index dc9bb73..b8aeb72 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/QueueSettingsEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/QueueSettingsEditor.vue
@@ -73,6 +73,7 @@
:max="maxNodes"
v-model="data.nodeCount"
required
+ @input="nodeCountChanged"
:state="getValidationState('nodeCount', true)"
>
</b-form-input>
@@ -94,12 +95,16 @@
: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 }}
+ Max Allowed Cores = {{ maxCPUCount
+ }}<template v-if="queue && queue.cpuPerNode > 0"
+ >. There are {{ queue.cpuPerNode }} cores per node.
+ </template>
</div>
</b-form-group>
<b-form-group
@@ -413,6 +418,26 @@
);
}
},
+ nodeCountChanged() {
+ if (this.selectedQueueDefault.cpuPerNode > 0) {
+ const nodeCount = parseInt(this.data.nodeCount);
+ this.data.totalCPUCount = Math.min(
+ nodeCount * this.selectedQueueDefault.cpuPerNode,
+ this.maxCPUCount
+ );
+ }
+ },
+ cpuCountChanged() {
+ if (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
+ );
+ }
+ }
+ },
},
watch: {
appDeploymentId() {
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue
index 3c2a83d..c62e546 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/QueueSettingsEditor.vue
@@ -75,7 +75,10 @@
</b-form-input>
<div slot="description">
<i class="fa fa-info-circle" aria-hidden="true"></i>
- Max Allowed Cores = {{ maxAllowedCores }}
+ Max Allowed Cores = {{ maxAllowedCores
+ }}<template v-if="queue && queue.cpuPerNode > 0"
+ >. There are {{ queue.cpuPerNode }} cores per node.
+ </template>
</div>
</b-form-group>
<b-form-group label="Wall Time Limit" label-for="walltime-limit">
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js
index 3ebdf43..2f1be91 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/web-components/store.js
@@ -198,11 +198,27 @@
commit("setLazyQueueName", { queueName });
}
},
- updateTotalCPUCount({ commit }, { totalCPUCount }) {
+ updateTotalCPUCount({ commit, getters }, { totalCPUCount }) {
commit("updateTotalCPUCount", { totalCPUCount });
+ if (getters.queue.cpuPerNode > 0) {
+ const totalCPUCountInt = parseInt(totalCPUCount);
+ const nodeCount = Math.min(
+ Math.ceil(totalCPUCountInt / getters.queue.cpuPerNode),
+ getters.maxAllowedNodes
+ );
+ commit("updateNodeCount", { nodeCount });
+ }
},
- updateNodeCount({ commit }, { nodeCount }) {
+ updateNodeCount({ commit, getters }, { nodeCount }) {
commit("updateNodeCount", { nodeCount });
+ if (getters.queue.cpuPerNode > 0) {
+ const nodeCountInt = parseInt(nodeCount);
+ const totalCPUCount = Math.min(
+ nodeCountInt * getters.queue.cpuPerNode,
+ getters.maxAllowedCores
+ );
+ commit("updateTotalCPUCount", { totalCPUCount });
+ }
},
updateWallTimeLimit({ commit }, { wallTimeLimit }) {
commit("updateWallTimeLimit", { wallTimeLimit });
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/web-components/store.spec.js b/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/web-components/store.spec.js
index f3cf381..8ff8967 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/web-components/store.spec.js
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/web-components/store.spec.js
@@ -706,3 +706,134 @@
done,
});
});
+
+test("updateNodeCount: only update nodeCount when cpuPerNode <= 0", (done) => {
+ const mockGetters = {
+ queue: new models.BatchQueue({
+ cpuPerNode: 0,
+ }),
+ };
+ const expectedMutations = [
+ { type: "updateNodeCount", payload: { nodeCount: 7 } },
+ ];
+ testAction(actions.updateNodeCount, {
+ payload: {
+ nodeCount: 7,
+ },
+ getters: mockGetters,
+ expectedMutations,
+ done,
+ });
+});
+
+test("updateNodeCount: update also totalCPUCount when cpuPerNode > 0", (done) => {
+ const nodeCount = 4;
+ const mockGetters = {
+ queue: new models.BatchQueue({
+ cpuPerNode: 24,
+ }),
+ maxAllowedCores: 1000,
+ };
+ const expectedMutations = [
+ { type: "updateNodeCount", payload: { nodeCount } },
+ { type: "updateTotalCPUCount", payload: { totalCPUCount: 96 } },
+ ];
+ testAction(actions.updateNodeCount, {
+ payload: {
+ nodeCount,
+ },
+ getters: mockGetters,
+ expectedMutations,
+ done,
+ });
+});
+
+test("updateNodeCount: update totalCPUCount when cpuPerNode > 0, but apply maximums", (done) => {
+ const nodeCount = 4;
+ const mockGetters = {
+ queue: new models.BatchQueue({
+ cpuPerNode: 24,
+ }),
+ maxAllowedCores: 50,
+ };
+ const expectedMutations = [
+ { type: "updateNodeCount", payload: { nodeCount } },
+ { type: "updateTotalCPUCount", payload: { totalCPUCount: 50 } },
+ ];
+ testAction(actions.updateNodeCount, {
+ payload: {
+ nodeCount,
+ },
+ getters: mockGetters,
+ expectedMutations,
+ done,
+ });
+});
+
+test("updateTotalCPUCount: only update totalCPUCount when cpuPerNode <= 0", (done) => {
+ const totalCPUCount = 23;
+ const mockGetters = {
+ queue: new models.BatchQueue({
+ cpuPerNode: 0,
+ }),
+ };
+ const expectedMutations = [
+ { type: "updateTotalCPUCount", payload: { totalCPUCount } },
+ ];
+ testAction(actions.updateTotalCPUCount, {
+ payload: {
+ totalCPUCount,
+ },
+ getters: mockGetters,
+ expectedMutations,
+ done,
+ });
+});
+
+test("updateTotalCPUCount: update also nodeCount when cpuPerNode > 0", (done) => {
+ const nodeCount = 4;
+ const totalCPUCount = 96;
+ const mockGetters = {
+ queue: new models.BatchQueue({
+ cpuPerNode: 24,
+ }),
+ maxAllowedNodes: 1000,
+ };
+ const expectedMutations = [
+ { type: "updateTotalCPUCount", payload: { totalCPUCount } },
+ { type: "updateNodeCount", payload: { nodeCount } },
+ ];
+ testAction(actions.updateTotalCPUCount, {
+ payload: {
+ totalCPUCount,
+ },
+ getters: mockGetters,
+ expectedMutations,
+ done,
+ });
+});
+
+test("updateTotalCPUCount: update nodeCount when cpuPerNode > 0, but apply maximums", (done) => {
+ const totalCPUCount = 96;
+ const mockGetters = {
+ queue: new models.BatchQueue({
+ cpuPerNode: 24,
+ }),
+ maxAllowedNodes: 2,
+ };
+ expect(totalCPUCount / mockGetters.queue.cpuPerNode).toBeGreaterThan(
+ mockGetters.maxAllowedNodes
+ );
+ const expectedMutations = [
+ { type: "updateTotalCPUCount", payload: { totalCPUCount } },
+ { type: "updateNodeCount", payload: { nodeCount: 2 } },
+ ];
+ testAction(actions.updateTotalCPUCount, {
+ payload: {
+ totalCPUCount,
+ },
+ getters: mockGetters,
+ expectedMutations,
+ done,
+ });
+});