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.
 
 
 
 
 
 

234 lines
7.6 KiB

import { Grid } from '@mui/material';
import { FC, useMemo, useRef, useState } from 'react';
import API_BASE_URL from '../../lib/consts/API_BASE_URL';
import NETWORK_TYPES from '../../lib/consts/NETWORK_TYPES';
import AddPeerDialog from './AddPeerDialog';
import api from '../../lib/api';
import ConfirmDialog from '../ConfirmDialog';
import FlexBox from '../FlexBox';
import handleAPIError from '../../lib/handleAPIError';
import List from '../List';
import MessageBox, { Message } from '../MessageBox';
import { ExpandablePanel } from '../Panels';
import periodicFetch from '../../lib/fetchers/periodicFetch';
import State from '../State';
import { BodyText, MonoText, SmallText } from '../Text';
import useProtect from '../../hooks/useProtect';
import useProtectedState from '../../hooks/useProtectedState';
const ConfigPeersForm: FC<ConfigPeerFormProps> = ({
refreshInterval = 60000,
}) => {
const { protect } = useProtect();
const addPeerDialogRef = useRef<ConfirmDialogForwardedRefContent>({});
const confirmDialogRef = useRef<ConfirmDialogForwardedRefContent>({});
const [apiMessage, setAPIMessage] = useProtectedState<Message | undefined>(
undefined,
protect,
);
const [confirmDialogProps, setConfirmDialogProps] =
useState<ConfirmDialogProps>({
actionProceedText: '',
content: '',
titleText: '',
});
const [inboundConnections, setInboundConnections] =
useProtectedState<InboundConnectionList>({}, protect);
const [isEditPeerConnections, setIsEditPeerConnections] =
useState<boolean>(false);
const [peerConnections, setPeerConnections] =
useProtectedState<PeerConnectionList>({}, protect);
const apiMessageElement = useMemo(
() =>
apiMessage && (
<Grid item sm={2} xs={1}>
<MessageBox {...apiMessage} />
</Grid>
),
[apiMessage],
);
const { isLoading } = periodicFetch<APIHostConnectionOverviewList>(
`${API_BASE_URL}/host/connection`,
{
refreshInterval,
onError: (error) => {
setAPIMessage({
children: `Failed to get connection data. Error: ${error}`,
type: 'error',
});
},
onSuccess: ({
local: {
inbound: { ipAddress: ipAddressList, port: dbPort, user: dbUser },
peer,
},
}) => {
setInboundConnections((previous) =>
Object.entries(ipAddressList).reduce<InboundConnectionList>(
(
nyu,
[ipAddress, { networkLinkNumber, networkNumber, networkType }],
) => {
nyu[ipAddress] = {
...previous[ipAddress],
dbPort,
dbUser,
ipAddress,
networkLinkNumber,
networkNumber,
networkType,
};
return nyu;
},
{},
),
);
setPeerConnections((previous) =>
Object.entries(peer).reduce<PeerConnectionList>(
(
nyu,
[
peerIPAddress,
{
hostUUID,
isPing: isPingTest,
port: peerDBPort,
user: peerDBUser,
},
],
) => {
const peerKey = `${peerDBUser}@${peerIPAddress}:${peerDBPort}`;
nyu[peerKey] = {
...previous[peerKey],
dbPort: peerDBPort,
dbUser: peerDBUser,
hostUUID,
ipAddress: peerIPAddress,
isPingTest,
};
return nyu;
},
{},
),
);
},
},
);
return (
<>
<ExpandablePanel header="Configure striker peers" loading={isLoading}>
<Grid columns={{ xs: 1, sm: 2 }} container spacing="1em">
<Grid item xs={1}>
<List
header="Inbound connections"
listEmpty={
<BodyText align="center">
No inbound connections found.
</BodyText>
}
listItemKeyPrefix="config-peers-inbound-connection"
listItems={inboundConnections}
renderListItem={(
ipAddress,
{ dbPort, dbUser, networkNumber, networkType },
) => (
<FlexBox spacing={0} sx={{ width: '100%' }}>
<MonoText>{`${dbUser}@${ipAddress}:${dbPort}`}</MonoText>
<SmallText>{`${NETWORK_TYPES[networkType]} ${networkNumber}`}</SmallText>
</FlexBox>
)}
/>
</Grid>
<Grid item xs={1}>
<List
header="Peer connections"
allowEdit
edit={isEditPeerConnections}
listEmpty={
<BodyText align="center">No peer connections found.</BodyText>
}
listItemKeyPrefix="config-peers-peer-connection"
listItems={peerConnections}
onAdd={() => {
addPeerDialogRef.current.setOpen?.call(null, true);
}}
onDelete={() => {
const pairs = Object.entries(peerConnections);
const deleteRequestBody =
pairs.reduce<APIDeleteHostConnectionRequestBody>(
(previous, [, { hostUUID, isChecked }]) => {
if (isChecked) {
previous.local.push(hostUUID);
}
return previous;
},
{ local: [] },
);
const deleteCount = deleteRequestBody.local.length;
if (deleteCount > 0) {
setConfirmDialogProps({
actionProceedText: 'Delete',
content: `The peer relationship between this striker and the selected ${deleteCount} host(s) will terminate. The removed peer(s) can be re-added later.`,
onProceedAppend: () => {
api
.delete('/host/connection', { data: deleteRequestBody })
.catch((error) => {
const emsg = handleAPIError(error);
emsg.children = `Failed to delete peer connection(s). ${emsg.children}`;
setAPIMessage(emsg);
});
},
proceedColour: 'red',
titleText: `Delete ${deleteCount} peer(s) from this striker?`,
});
confirmDialogRef.current.setOpen?.call(null, true);
}
}}
onEdit={() => {
setIsEditPeerConnections((previous) => !previous);
}}
onItemCheckboxChange={(key, event, isChecked) => {
peerConnections[key].isChecked = isChecked;
setPeerConnections((previous) => ({ ...previous }));
}}
renderListItem={(peer, { isPingTest = false }) => (
<FlexBox row spacing={0}>
<FlexBox spacing={0}>
<MonoText>{peer}</MonoText>
<State label="Ping" state={isPingTest} />
</FlexBox>
</FlexBox>
)}
/>
</Grid>
{apiMessageElement}
</Grid>
</ExpandablePanel>
<AddPeerDialog ref={addPeerDialogRef} />
<ConfirmDialog
closeOnProceed
{...confirmDialogProps}
ref={confirmDialogRef}
/>
</>
);
};
export default ConfigPeersForm;