Local modifications to ClusterLabs/Anvil by Alteeve
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

413 lines
12 KiB

import { ReactElement, useMemo } from 'react';
import FlexBox from '../FlexBox';
import Grid from '../Grid';
import InputWithRef from '../InputWithRef';
import MessageBox from '../MessageBox';
import OutlinedInputWithLabel from '../OutlinedInputWithLabel';
import { InnerPanel, InnerPanelBody, InnerPanelHeader } from '../Panels';
import SwitchWithLabel from '../SwitchWithLabel';
import {
buildIPAddressTestBatch,
buildPeacefulStringTestBatch,
} from '../../lib/test_input';
import { BodyText } from '../Text';
const INPUT_ID_PREFIX_AN_HOST = 'an-host-input';
const INPUT_CELL_ID_PREFIX_AH = `${INPUT_ID_PREFIX_AN_HOST}-cell`;
const INPUT_LABEL_AH_IPMI_IP = 'IPMI IP';
const MAP_TO_AH_INPUT_HANDLER: MapToManifestFormInputHandler = {
fence: (container, input) => {
const {
dataset: { hostId = '', fenceId = '', fenceName = '' },
value: fencePort,
} = input;
const {
hostConfig: {
hosts: { [hostId]: host },
},
} = container;
const { fences = {} } = host;
fences[fenceId] = {
fenceName,
fencePort,
};
host.fences = fences;
},
host: (container, input) => {
const {
dataset: { hostId = '', hostNumber: rawHostNumber = '', hostType = '' },
} = input;
const hostNumber = Number.parseInt(rawHostNumber, 10);
container.hostConfig.hosts[hostId] = {
hostNumber,
hostType,
};
},
ipmi: (container, input) => {
const {
dataset: { hostId = '' },
value: ipmiIp,
} = input;
const {
hostConfig: {
hosts: { [hostId]: host },
},
} = container;
host.ipmiIp = ipmiIp;
},
network: (container, input) => {
const {
dataset: {
hostId = '',
networkId = '',
networkNumber: rawNetworkNumber = '',
networkType = '',
},
value: networkIp,
} = input;
const {
hostConfig: {
hosts: { [hostId]: host },
},
} = container;
const { networks = {} } = host;
const networkNumber = Number.parseInt(rawNetworkNumber, 10);
networks[networkId] = {
networkIp,
networkNumber,
networkType,
};
host.networks = networks;
},
ups: (container, input) => {
const {
checked: isUsed,
dataset: { hostId = '', upsId = '', upsName = '' },
} = input;
const {
hostConfig: {
hosts: { [hostId]: host },
},
} = container;
const { upses = {} } = host;
upses[upsId] = {
isUsed,
upsName,
};
host.upses = upses;
},
};
const GRID_COLUMNS = { xs: 1, sm: 2, md: 3 };
const GRID_SPACING = '1em';
const buildInputIdAHFencePort = (hostId: string, fenceId: string): string =>
`${INPUT_ID_PREFIX_AN_HOST}-${hostId}-${fenceId}-port`;
const buildInputIdAHIpmiIp = (hostId: string): string =>
`${INPUT_ID_PREFIX_AN_HOST}-${hostId}-ipmi-ip`;
const buildInputIdAHNetworkIp = (hostId: string, networkId: string): string =>
`${INPUT_ID_PREFIX_AN_HOST}-${hostId}-${networkId}-ip`;
const buildInputIdAHUpsPowerHost = (hostId: string, upsId: string): string =>
`${INPUT_ID_PREFIX_AN_HOST}-${hostId}-${upsId}-power-host`;
const AnHostInputGroup = <M extends MapToInputTestID>({
formUtils: {
buildFinishInputTestBatchFunction,
buildInputFirstRenderFunction,
buildInputUnmountFunction,
setMessage,
},
hostId,
hostNumber,
hostType,
previous: {
fences: fenceList = {},
ipmiIp: previousIpmiIp,
networks: networkList = {},
upses: upsList = {},
} = {},
// Props that depend on others.
hostLabel = `${hostType.replace('node', 'subnode')} ${hostNumber}`,
}: AnHostInputGroupProps<M>): ReactElement => {
const fenceListEntries = useMemo(
() => Object.entries(fenceList),
[fenceList],
);
const networkListEntries = useMemo(
() => Object.entries(networkList),
[networkList],
);
const upsListEntries = useMemo(() => Object.entries(upsList), [upsList]);
const isShowUpsListGrid = useMemo(
() => Boolean(upsListEntries.length),
[upsListEntries.length],
);
const inputIdAHHost = useMemo(
() => `${INPUT_ID_PREFIX_AN_HOST}-${hostId}`,
[hostId],
);
const inputIdAHIpmiIp = useMemo(() => buildInputIdAHIpmiIp(hostId), [hostId]);
const inputCellIdAHIpmiIp = useMemo(
() => `${INPUT_CELL_ID_PREFIX_AH}-${hostId}-ipmi-ip`,
[hostId],
);
const fenceListGridLayout = useMemo(
() =>
fenceListEntries.reduce<GridLayout>(
(previous, [fenceId, { fenceName, fencePort }]) => {
const cellId = `${INPUT_CELL_ID_PREFIX_AH}-${hostId}-${fenceId}-port`;
const inputId = buildInputIdAHFencePort(hostId, fenceId);
const inputLabel = `Port on ${fenceName}`;
previous[cellId] = {
children: (
<InputWithRef
input={
<OutlinedInputWithLabel
baseInputProps={{
'data-handler': 'fence',
'data-host-id': hostId,
'data-fence-id': fenceId,
'data-fence-name': fenceName,
}}
id={inputId}
label={inputLabel}
value={fencePort}
/>
}
inputTestBatch={buildPeacefulStringTestBatch(
`${hostId} ${inputLabel}`,
() => {
setMessage(inputId);
},
{ onFinishBatch: buildFinishInputTestBatchFunction(inputId) },
(message) => {
setMessage(inputId, { children: message });
},
)}
onFirstRender={buildInputFirstRenderFunction(inputId)}
/>
),
};
return previous;
},
{},
),
[
buildFinishInputTestBatchFunction,
buildInputFirstRenderFunction,
fenceListEntries,
hostId,
setMessage,
],
);
const networkListGridLayout = useMemo(
() =>
networkListEntries.reduce<GridLayout>(
(previous, [networkId, { networkIp, networkNumber, networkType }]) => {
const cellId = `${INPUT_CELL_ID_PREFIX_AH}-${hostId}-${networkId}-ip`;
const inputId = buildInputIdAHNetworkIp(hostId, networkId);
const inputLabel = `${networkType.toUpperCase()} ${networkNumber} IP`;
previous[cellId] = {
children: (
<InputWithRef
input={
<OutlinedInputWithLabel
baseInputProps={{
'data-handler': 'network',
'data-host-id': hostId,
'data-network-id': networkId,
'data-network-number': networkNumber,
'data-network-type': networkType,
}}
id={inputId}
label={inputLabel}
value={networkIp}
/>
}
inputTestBatch={buildIPAddressTestBatch(
`${hostId} ${inputLabel}`,
() => {
setMessage(inputId);
},
{ onFinishBatch: buildFinishInputTestBatchFunction(inputId) },
(message) => {
setMessage(inputId, { children: message });
},
)}
onFirstRender={buildInputFirstRenderFunction(inputId)}
onUnmount={buildInputUnmountFunction(inputId)}
required
/>
),
};
return previous;
},
{},
),
[
networkListEntries,
hostId,
buildFinishInputTestBatchFunction,
buildInputFirstRenderFunction,
buildInputUnmountFunction,
setMessage,
],
);
const upsListGridLayout = useMemo(
() =>
upsListEntries.reduce<GridLayout>(
(previous, [upsId, { isUsed, upsName }]) => {
const cellId = `${INPUT_CELL_ID_PREFIX_AH}-${hostId}-${upsId}-power-host`;
const inputId = buildInputIdAHUpsPowerHost(hostId, upsId);
const inputLabel = `Uses ${upsName}`;
previous[cellId] = {
children: (
<InputWithRef
input={
<SwitchWithLabel
baseInputProps={{
'data-handler': 'ups',
'data-host-id': hostId,
'data-ups-id': upsId,
'data-ups-name': upsName,
}}
checked={isUsed}
id={inputId}
label={inputLabel}
/>
}
valueType="boolean"
/>
),
};
return previous;
},
{},
),
[hostId, upsListEntries],
);
const upsListGrid = useMemo(
() =>
isShowUpsListGrid && (
<Grid
columns={GRID_COLUMNS}
layout={upsListGridLayout}
spacing={GRID_SPACING}
/>
),
[isShowUpsListGrid, upsListGridLayout],
);
return (
<InnerPanel mv={0}>
<InnerPanelHeader>
<BodyText>{hostLabel}</BodyText>
</InnerPanelHeader>
<InnerPanelBody>
<input
hidden
id={inputIdAHHost}
readOnly
data-handler="host"
data-host-id={hostId}
data-host-number={hostNumber}
data-host-type={hostType}
/>
<FlexBox>
<Grid
columns={GRID_COLUMNS}
layout={{
'fence-message': {
children: (
<MessageBox>
It is recommended to provide 2 fence device ports.
</MessageBox>
),
width: '100%',
xs: 0,
},
...networkListGridLayout,
[inputCellIdAHIpmiIp]: {
children: (
<InputWithRef
input={
<OutlinedInputWithLabel
baseInputProps={{
'data-handler': 'ipmi',
'data-host-id': hostId,
}}
id={inputIdAHIpmiIp}
label={INPUT_LABEL_AH_IPMI_IP}
value={previousIpmiIp}
/>
}
inputTestBatch={buildIPAddressTestBatch(
`${hostId} ${INPUT_LABEL_AH_IPMI_IP}`,
() => {
setMessage(inputIdAHIpmiIp);
},
{
onFinishBatch:
buildFinishInputTestBatchFunction(inputIdAHIpmiIp),
},
(message) => {
setMessage(inputIdAHIpmiIp, { children: message });
},
)}
onFirstRender={buildInputFirstRenderFunction(
inputIdAHIpmiIp,
)}
onUnmount={buildInputUnmountFunction(inputIdAHIpmiIp)}
/>
),
},
...fenceListGridLayout,
}}
spacing={GRID_SPACING}
/>
{upsListGrid}
</FlexBox>
</InnerPanelBody>
</InnerPanel>
);
};
export {
INPUT_ID_PREFIX_AN_HOST,
MAP_TO_AH_INPUT_HANDLER,
buildInputIdAHFencePort,
buildInputIdAHIpmiIp,
buildInputIdAHNetworkIp,
buildInputIdAHUpsPowerHost,
};
export default AnHostInputGroup;