import { ColumnLayout, FormField, Input, Multiselect, MultiselectProps, Checkbox, SpaceBetween, TextContent, Button, Box, } from '@cloudscape-design/components' import {NonCancelableEventHandler} from '@cloudscape-design/components/internal/events' import {useCallback, useEffect, useMemo} from 'react' import {Trans, useTranslation} from 'react-i18next' import {clearState, setState, useState} from '../../../store' import { CheckboxWithHelpPanel, HelpTextInput, useInstanceGroups, } from '../Components' import { ComputeResourceInstance, MultiInstanceComputeResource, QueueValidationErrors, } from './queues.types' import {InstanceType} from '../Components.types' import TitleDescriptionHelpPanel from '../../../components/help-panel/TitleDescriptionHelpPanel' import componentsStyle from '../Components.module.css' const queuesPath = ['app', 'wizard', 'config', 'Scheduling', 'SlurmQueues'] const queuesErrorsPath = ['app', 'wizard', 'errors', 'queues'] const defaultInstanceType = 'c5n.large' export function allInstancesSupportEFA( instances: ComputeResourceInstance[], efaInstances: Set, ): boolean { if (!instances || !instances.length) { return false } return instances.every(instance => efaInstances.has(instance.InstanceType)) } function isHpcInstanceSelected(instances: ComputeResourceInstance[]): boolean { if (!instances || !instances.length) { return false } return instances.some(instance => instance.InstanceType.startsWith('hpc')) } export function ComputeResource({ index, queueIndex, computeResource, canUseEFA, }: any) { const parentPath = useMemo(() => [...queuesPath, queueIndex], [queueIndex]) const computeResources: MultiInstanceComputeResource[] = useState([ ...parentPath, 'ComputeResources', ]) const path = useMemo( () => [...parentPath, 'ComputeResources', index], [index, parentPath], ) const errorsPath = [...queuesErrorsPath, queueIndex, 'computeResource', index] const typeError = useState([...errorsPath, 'type']) const instanceTypePath = useMemo(() => [...path, 'Instances'], [path]) const instances: ComputeResourceInstance[] = useState(instanceTypePath) || [] const memoryBasedSchedulingEnabledPath = [ 'app', 'wizard', 'config', 'Scheduling', 'SlurmSettings', 'EnableMemoryBasedScheduling', ] const enableMemoryBasedScheduling = useState(memoryBasedSchedulingEnabledPath) const multithreadingDisabledPath = useMemo( () => [...path, 'DisableSimultaneousMultithreading'], [path], ) const multithreadingDisabled = useState(multithreadingDisabledPath) const efaInstances = new Set(useState(['aws', 'efa_instance_types'])) const enableEFA = useState([...path, 'Efa', 'Enabled']) || false const enablePlacementGroupPath = useMemo( () => [...parentPath, 'Networking', 'PlacementGroup', 'Enabled'], [parentPath], ) const minCount = useState([...path, 'MinCount']) const maxCount = useState([...path, 'MaxCount']) const instanceGroups = useInstanceGroups() const instanceOptions = useMemo( () => Object.keys(instanceGroups).map(groupName => { return { label: groupName, options: instanceGroups[groupName].map((instance: InstanceType) => ({ label: instance.type, tags: instance.tags, value: instance.type, })), } }), [instanceGroups], ) const {t} = useTranslation() const remove = () => { setState( [...parentPath, 'ComputeResources'], [ ...computeResources.slice(0, index), ...computeResources.slice(index + 1), ], ) } const setMinCount = (staticCount: any) => { const dynamicCount = maxCount - minCount if (staticCount > 0) setState([...path, 'MinCount'], !isNaN(staticCount) ? staticCount : 0) else clearState([...path, 'MinCount']) setState( [...path, 'MaxCount'], (!isNaN(staticCount) ? staticCount : 0) + (!isNaN(dynamicCount) ? dynamicCount : 0), ) } const setMaxCount = (dynamicCount: any) => { const staticCount = minCount setState( [...path, 'MaxCount'], (!isNaN(staticCount) ? staticCount : 0) + (!isNaN(dynamicCount) ? dynamicCount : 0), ) } const setSchedulableMemory = ( schedulableMemoryPath: string[], schedulableMemory: string, ) => { let schedulableMemoryNumber = parseInt(schedulableMemory) if (enableMemoryBasedScheduling && !isNaN(schedulableMemoryNumber)) { setState(schedulableMemoryPath, schedulableMemoryNumber) } else { clearState(schedulableMemoryPath) } } const setDisableHT = useCallback( (disable: any) => { if (disable) setState(multithreadingDisabledPath, disable) else clearState(multithreadingDisabledPath) }, [multithreadingDisabledPath], ) const setEnableEFA = useCallback( (enable: any) => { if (enable) { setState([...path, 'Efa', 'Enabled'], enable) setState(enablePlacementGroupPath, enable) } else { clearState([...path, 'Efa']) clearState(enablePlacementGroupPath) } }, [enablePlacementGroupPath, path], ) const hpcInstanceSelected = isHpcInstanceSelected(instances) useEffect(() => { if (!canUseEFA) { setEnableEFA(false) } }, [canUseEFA, setEnableEFA]) useEffect(() => { if (hpcInstanceSelected) { setDisableHT(false) } }, [hpcInstanceSelected, setDisableHT]) const setInstances: NonCancelableEventHandler = useCallback( ({detail}) => { const selectedInstances = (detail.selectedOptions.map(option => ({ InstanceType: option.value, })) || []) as ComputeResourceInstance[] setState(instanceTypePath, selectedInstances) if (!allInstancesSupportEFA(selectedInstances, efaInstances)) { setEnableEFA(false) } }, [efaInstances, instanceTypePath, setEnableEFA], ) return (

{t('wizard.queues.computeResource.name', { index: index + 1, crName: computeResource.Name, })}

{computeResources.length > 1 && ( )}
setMinCount(parseInt(detail.value))} /> setMaxCount(parseInt(detail.value))} /> ({ value: instance.InstanceType, label: instance.InstanceType, }))} placeholder={t( 'wizard.queues.computeResource.instanceType.placeholder.multiple', )} tokenLimit={3} onChange={setInstances} options={instanceOptions} filteringType="auto" /> {enableMemoryBasedScheduling && ( setSchedulableMemory([...path, 'SchedulableMemory'], detail.value) } description={t('wizard.queues.schedulableMemory.description')} placeholder={t('wizard.queues.schedulableMemory.placeholder')} help={t('wizard.queues.schedulableMemory.help')} type="number" /> )} { setDisableHT(!multithreadingDisabled) }} helpPanel={ } > { setEnableEFA(!enableEFA) }} >
) } export function createComputeResource( queueName: string, crIndex: number, ): MultiInstanceComputeResource { return { Name: `${queueName}-cr-${crIndex + 1}`, Instances: [ { InstanceType: defaultInstanceType, }, ], MinCount: 0, MaxCount: 4, } } export function updateComputeResourcesNames( computeResources: MultiInstanceComputeResource[], newQueueName: string, ): MultiInstanceComputeResource[] { return computeResources.map((cr, i) => ({ ...cr, Name: `${newQueueName}-cr-${i}`, })) } export function validateComputeResources( computeResources: MultiInstanceComputeResource[], ): [boolean, QueueValidationErrors] { let errors = computeResources.reduce((acc, cr, i) => { if (!cr.Instances || !cr.Instances.length) { acc[i] = 'instance_types_empty' } return acc }, {}) return [Object.keys(errors).length === 0, errors] }