Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/WP-80 Implement dynamic execution system selection #921

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
19c02c3
Working version of execution system changes
chandra-tacc Nov 17, 2023
31ae843
Add tests, fix issues found from unit tests
chandra-tacc Dec 17, 2023
95558cf
Fix job history and also lint
chandra-tacc Dec 18, 2023
560aedf
Use one attribute for exec systems instead of two
chandra-tacc Feb 2, 2024
b64317b
Merge branch 'main' into task/WP-80-execution-system
chandra-tacc Feb 10, 2024
9c3d577
Fix formatting
chandra-tacc Feb 10, 2024
e91c26b
Merge branch 'main' into task/WP-80-execution-system
chandra-tacc Feb 13, 2024
4c4251b
Sort system list in UI
chandra-tacc Feb 14, 2024
460de43
Merge branch 'main' into task/WP-80-execution-system
rstijerina Feb 27, 2024
e4242b4
Address code review comments
chandra-tacc Feb 28, 2024
3ef9a53
Adjusted exec system label text and fixed jest tests
chandra-tacc Mar 1, 2024
9014370
Working version of execution system changes
chandra-tacc Nov 17, 2023
ffa33a6
Add tests, fix issues found from unit tests
chandra-tacc Dec 17, 2023
d1146f6
Fix job history and also lint
chandra-tacc Dec 18, 2023
27e9476
Use one attribute for exec systems instead of two
chandra-tacc Feb 2, 2024
5bd7a39
Fix formatting
chandra-tacc Feb 10, 2024
e8acb8e
Sort system list in UI
chandra-tacc Feb 14, 2024
f5271ab
Address code review comments
chandra-tacc Feb 28, 2024
8be2d32
Adjusted exec system label text and fixed jest tests
chandra-tacc Mar 1, 2024
b2fad67
Fix bug related to job history execution system
chandra-tacc Mar 21, 2024
5de01b2
Merge branch 'task/WP-80-execution-system' of github.com:TACC/Core-Po…
chandra-tacc Mar 21, 2024
f6dcb19
Merge fix
chandra-tacc Mar 21, 2024
c31bc64
Redo the fix on job history
chandra-tacc Mar 21, 2024
ffaf67e
Rework commit for exec and allocation
chandra-tacc Apr 4, 2024
11b5ed9
Prettier, lint and test fix
chandra-tacc Apr 17, 2024
ed22ec7
Merge branch 'main' into task/WP-80-execution-system
chandra-tacc Apr 17, 2024
6d0bf31
Merge branch 'main' into task/WP-80-execution-system
chandra-tacc Apr 18, 2024
e33ccbf
Fix merge issues
chandra-tacc Apr 18, 2024
7a85c9d
Make exec system dependent on allocation
chandra-tacc Apr 30, 2024
3a2920f
Merge branch 'main' into task/WP-80-execution-system
chandra-tacc May 3, 2024
cd616d5
Fix job history display of system name
chandra-tacc May 3, 2024
ce7a917
Merge branch 'main' into task/WP-80-execution-system
chandra-tacc May 16, 2024
bfd9869
Handle max memory on a system
chandra-tacc May 17, 2024
0e30fe4
Merge branch 'task/WP-80-execution-system' of github.com:TACC/Core-Po…
chandra-tacc May 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
291 changes: 197 additions & 94 deletions client/src/components/Applications/AppForm/AppForm.jsx

Large diffs are not rendered by default.

488 changes: 479 additions & 9 deletions client/src/components/Applications/AppForm/AppForm.test.js

Large diffs are not rendered by default.

189 changes: 184 additions & 5 deletions client/src/components/Applications/AppForm/AppFormUtils.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import * as Yup from 'yup';
import { getSystemName } from 'utils/systems';
import { getExecSystemFromId } from 'utils/apps';

export const TARGET_PATH_FIELD_PREFIX = '_TargetPath_';
export const DEFAULT_JOB_MAX_MINUTES = 60 * 24;

export const getQueueMaxMinutes = (app, queueName) => {
export const getQueueMaxMinutes = (app, exec_sys, queueName) => {
if (!isJobTypeBATCH(app)) {
return DEFAULT_JOB_MAX_MINUTES;
}

return app.exec_sys.batchLogicalQueues.find((q) => q.name === queueName)
.maxMinutes;
return (
exec_sys?.batchLogicalQueues.find((q) => q.name === queueName)
?.maxMinutes ?? 0
);
};

/**
Expand All @@ -24,6 +26,10 @@ export const getMaxMinutesValidation = (queue, app) => {
if (!isJobTypeBATCH(app)) {
return Yup.number().max(DEFAULT_JOB_MAX_MINUTES);
}
if (!queue) {
return Yup.number();
}

return Yup.number()
.min(
queue.minMinutes,
Expand Down Expand Up @@ -91,6 +97,9 @@ export const createMaxRunTimeRegex = (maxRunTime) => {
* @returns {Yup.number()} min/max validation of node count
*/
export const getNodeCountValidation = (queue) => {
if (!queue) {
return Yup.number();
}
return Yup.number()
.integer('Node Count must be an integer.')
.min(
Expand All @@ -111,6 +120,9 @@ export const getNodeCountValidation = (queue) => {
* @returns {Yup.number()} min/max validation of coresPerNode
*/
export const getCoresPerNodeValidation = (queue) => {
if (!queue) {
return Yup.number();
}
if (queue.maxCoresPerNode === -1) {
return Yup.number().integer();
}
Expand All @@ -131,8 +143,12 @@ export const getCoresPerNodeValidation = (queue) => {
* @returns {Object} updated/fixed values
*/
export const updateValuesForQueue = (app, values) => {
const exec_sys = getExecSystemFromId(app, values.execSystemId);
if (!exec_sys) {
return values;
}
const updatedValues = { ...values };
const queue = app.exec_sys.batchLogicalQueues.find(
const queue = exec_sys.batchLogicalQueues.find(
(q) => q.name === values.execSystemLogicalQueue
);

Expand Down Expand Up @@ -176,6 +192,116 @@ export const updateValuesForQueue = (app, values) => {
return updatedValues;
};

/**
* Handle exec system changes in the App Form.
* @param {*} app
* @param {*} values
* @param {*} formStateUpdateHandler
* @returns updatedValues
*/
export const execSystemChangeHandler = (
app,
values,
formStateUpdateHandler
) => {
const exec_sys = getExecSystemFromId(app, values.execSystemId);
let updatedValues = { ...values };
updatedValues.execSystemLogicalQueue = getQueueValueForExecSystem(
app,
exec_sys
)?.name;
updatedValues = updateValuesForQueue(app, updatedValues);
formStateUpdateHandler.setAppQueueValues(
getAppQueueValues(app, exec_sys?.batchLogicalQueues)
);
return updatedValues;
};

/**
* Get the default queue for a execution system.
* Queue Name determination order:
* 1. Use given queue name.
* 2. Otherwise, use the app default queue.
* 3. Otherwise, use the execution system default queue.
*
* @function
* @param {any} app App Shape defined in AppForm.jsx
* @param {any} exec_sys execution system, shape defined in AppForm.jsx
* @returns {String} queue_name nullable, queue name to lookup
*/
export const getQueueValueForExecSystem = (app, exec_sys, queue_name) => {
const queueName =
queue_name ??
app.definition.jobAttributes.execSystemLogicalQueue ??
exec_sys?.batchDefaultLogicalQueue;
return (
exec_sys?.batchLogicalQueues.find((q) => q.name === queueName) ||
exec_sys?.batchLogicalQueues[0]
);
};

/**
* Apply the following two filters and get the list of queues applicable.
* Filters:
* 1. If Node and Core per Node is enabled, only allow
* queues which match min and max node count with job attributes
* 2. if queue filter list is set, only allow queues in that list.
* @returns list of queues in sorted order
*/
export const getAppQueueValues = (app, queues) => {
/*
Hide queues for which the app default nodeCount does not meet the minimum or maximum requirements
while hideNodeCountAndCoresPerNode is true
*/
return (queues ?? [])
.filter(
(q) =>
!app.definition.notes.hideNodeCountAndCoresPerNode ||
(app.definition.jobAttributes.nodeCount >= q.minNodeCount &&
app.definition.jobAttributes.nodeCount <= q.maxNodeCount)
)
.map((q) => q.name)
.filter((queueName) =>
app.definition.notes.queueFilter
? app.definition.notes.queueFilter.includes(queueName)
: true
)
.sort();
};

/**
* Builds a Map of Allocation project names to exec system id's supported
* by the allocation
* @param {*} app
* @param {*} allocations
* @returns a Map of Allocation project id to exec system id
*/
export const buildMapOfAllocationsToExecSystems = (app, allocations) => {
const allocationToExecSystems = new Map();

Object.entries(allocations.hosts).forEach(([host, allocationsForHost]) => {
allocationsForHost.forEach((allocation) => {
const matchingExecutionHosts = [];
app.execSystems.forEach((exec_sys) => {
if (exec_sys.host === host || exec_sys.host.endsWith(`.${host}`)) {
matchingExecutionHosts.push(exec_sys.id);
}
});

if (matchingExecutionHosts.length > 0) {
const existingAllocations =
allocationToExecSystems.get(allocation) || [];
allocationToExecSystems.set(allocation, [
...existingAllocations,
...matchingExecutionHosts,
]);
}
});
});

return allocationToExecSystems;
};

/**
* Get the field name used for target path in AppForm
*
Expand Down Expand Up @@ -245,12 +371,65 @@ export const checkAndSetDefaultTargetPath = (targetPathFieldValue) => {
return targetPathFieldValue;
};

/**
* @param {*} app
* @returns True, if notes section in app definition has dynamicExecSystems property.
* Otherwise, false.
*/
export const isAppUsingDynamicExecSystem = (app) => {
return !!app.definition.notes.dynamicExecSystems;
};

/**
* @param {*} allocations
* @returns Yup validation schema for allocation
*/
export const getAllocationValidation = (allocations) => {
return Yup.string()
.required('Required')
.oneOf(allocations, 'Please select an allocation from the dropdown.');
};

/**
* @param {*} app
* @returns true, if the job type is BATCH, otherwise false.
*/
export const isJobTypeBATCH = (app) => {
return app.definition.jobType === 'BATCH';
};

/**
* Build list of all allocation if using dynamic exec syste,
* Otherwise, only provides allocation list matching
* the execution host.
* @param {*} app
* @param {*} allocations
* @returns List of type String
*/
export const getAllocationList = (app, allocations) => {
if (isAppUsingDynamicExecSystem(app)) {
return allocations.active.map((alloc) => alloc['projectName']);
}

const matchingExecutionHost = Object.keys(allocations.hosts).find(
(host) =>
app.execSystems[0].host === host ||
app.execSystems[0].host.endsWith(`.${host}`)
);

return matchingExecutionHost ? allocations.hosts[matchingExecutionHost] : [];
};

/**
* @param {*} allocationList
* @param {*} portalAlloc
* @returns portalAlloc if available, otherwise first item in allocation list.
* If list is empty, returns empty string.
*/
export const getDefaultAllocation = (allocationList, portalAlloc) => {
if (allocationList.includes(portalAlloc)) {
return portalAlloc;
}

return allocationList.length === 1 ? allocationList[0] : '';
};
15 changes: 9 additions & 6 deletions client/src/components/Applications/AppForm/AppFormUtils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { helloWorldAppFixture } from './fixtures/AppForm.app.fixture';
import { getNodeCountValidation, updateValuesForQueue } from './AppFormUtils';

describe('AppFormUtils', () => {
const normalQueue = helloWorldAppFixture.exec_sys.batchLogicalQueues.find(
(q) => q.name === 'normal'
);
const smallQueue = helloWorldAppFixture.exec_sys.batchLogicalQueues.find(
(q) => q.name === 'small'
);
const normalQueue =
helloWorldAppFixture.execSystems[0].batchLogicalQueues.find(
(q) => q.name === 'normal'
);
const smallQueue =
helloWorldAppFixture.execSystems[0].batchLogicalQueues.find(
(q) => q.name === 'small'
);

const parallelFronteraApp = {
...helloWorldAppFixture,
Expand Down Expand Up @@ -37,6 +39,7 @@ describe('AppFormUtils', () => {
nodeCount: 1,
coresPerNode: 1,
maxMinutes: '',
execSystemId: 'frontera',
};

it('handles node count validation', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,23 @@ const allocationsFixture = {
'frontera.tacc.utexas.edu': ['TACC-ACI'],
},
portal_alloc: 'TACC-ACI',
active: [
{
title: 'TACC-ACI',
projectId: 9192,
projectName: 'TACC-ACI',
systems: [
{
name: 'ls6',
host: 'ls6.tacc.utexas.edu',
},
{
name: 'frontera',
host: 'frontera.tacc.utexas.edu',
},
],
},
],
};

export default allocationsFixture;