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.
366 lines
10 KiB
366 lines
10 KiB
import { useRouter } from 'next/router'; |
|
import { FC, useCallback, useEffect, useMemo, useRef } from 'react'; |
|
|
|
import api from '../lib/api'; |
|
import ConfirmDialog from './ConfirmDialog'; |
|
import ContainedButton from './ContainedButton'; |
|
import FlexBox from './FlexBox'; |
|
import FormSummary from './FormSummary'; |
|
import getQueryParam from '../lib/getQueryParam'; |
|
import handleAPIError from '../lib/handleAPIError'; |
|
import InputWithRef from './InputWithRef'; |
|
import MessageBox, { Message } from './MessageBox'; |
|
import MessageGroup, { MessageGroupForwardedRefContent } from './MessageGroup'; |
|
import NetworkInitForm, { |
|
NetworkInitFormForwardedRefContent, |
|
} from './NetworkInitForm'; |
|
import OutlinedInputWithLabel from './OutlinedInputWithLabel'; |
|
import { Panel, PanelHeader } from './Panels'; |
|
import setMapNetwork from '../lib/setMapNetwork'; |
|
import Spinner from './Spinner'; |
|
import { buildPeacefulStringTestBatch } from '../lib/test_input'; |
|
import { HeaderText } from './Text'; |
|
import useConfirmDialogProps from '../hooks/useConfirmDialogProps'; |
|
import useFormUtils from '../hooks/useFormUtils'; |
|
import useProtectedState from '../hooks/useProtectedState'; |
|
|
|
const INPUT_ID_PREP_NET_HOST_NAME = 'prepare-network-host-name-input'; |
|
|
|
const INPUT_GROUP_ID_PREP_NET_NETCONF = 'prepare-network-netconf-input-group'; |
|
|
|
const INPUT_LABEL_PREP_NET_HOST_NAME = 'Host name'; |
|
|
|
const getFormData = ( |
|
{ |
|
netconf, |
|
}: { |
|
netconf: NetworkInitFormForwardedRefContent; |
|
}, |
|
...[{ target }]: DivFormEventHandlerParameters |
|
) => { |
|
const { elements } = target as HTMLFormElement; |
|
|
|
const { value: hostName } = elements.namedItem( |
|
INPUT_ID_PREP_NET_HOST_NAME, |
|
) as HTMLInputElement; |
|
|
|
const data = { |
|
hostName, |
|
...netconf.get?.call(null), |
|
}; |
|
|
|
return data; |
|
}; |
|
|
|
const toFormEntries = (body: ReturnType<typeof getFormData>): FormEntries => { |
|
const { networks: nets = [], ...rest } = body; |
|
|
|
const networks = nets.reduce<FormEntries>((previous, network) => { |
|
const { |
|
interfaces: ifaces, |
|
ipAddress, |
|
name = '', |
|
type, |
|
typeCount, |
|
subnetMask, |
|
} = network; |
|
const networkId = `${type}${typeCount}`; |
|
|
|
const interfaces = ifaces.reduce<FormEntries>((pIfaces, iface, index) => { |
|
if (iface) { |
|
const { networkInterfaceName } = iface; |
|
const linkNumber = index + 1; |
|
|
|
pIfaces[`link${linkNumber}`] = networkInterfaceName; |
|
} |
|
|
|
return pIfaces; |
|
}, {}); |
|
|
|
previous[networkId] = { |
|
name, |
|
network: `${ipAddress}/${subnetMask}`, |
|
...interfaces, |
|
}; |
|
|
|
return previous; |
|
}, {}); |
|
|
|
return { ...rest, networks }; |
|
}; |
|
|
|
const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({ |
|
expectUUID: isExpectExternalHostUUID = false, |
|
hostUUID, |
|
}) => { |
|
const { |
|
isReady, |
|
query: { host_uuid: queryHostUUID }, |
|
} = useRouter(); |
|
|
|
const confirmDialogRef = useRef<ConfirmDialogForwardedRefContent>({}); |
|
const netconfFormRef = useRef<NetworkInitFormForwardedRefContent>({}); |
|
|
|
const generalInputMessageGroupRef = useRef<MessageGroupForwardedRefContent>( |
|
{}, |
|
); |
|
|
|
const [confirmDialogProps, setConfirmDialogProps] = useConfirmDialogProps(); |
|
|
|
const [hostDetail, setHostDetail] = useProtectedState< |
|
APIHostDetail | undefined |
|
>(undefined); |
|
const [fatalErrorMessage, setFatalErrorMessage] = useProtectedState< |
|
Message | undefined |
|
>(undefined); |
|
const [isLoadingHostDetail, setIsLoadingHostDetail] = |
|
useProtectedState<boolean>(true); |
|
const [previousHostUUID, setPreviousHostUUID] = |
|
useProtectedState<PrepareNetworkFormProps['hostUUID']>(undefined); |
|
|
|
const { |
|
buildFinishInputTestBatchFunction, |
|
buildInputFirstRenderFunction, |
|
isFormInvalid, |
|
setMessage, |
|
setValidity, |
|
submitForm, |
|
} = useFormUtils( |
|
[INPUT_ID_PREP_NET_HOST_NAME, INPUT_GROUP_ID_PREP_NET_NETCONF], |
|
generalInputMessageGroupRef, |
|
); |
|
|
|
const isDifferentHostUUID = useMemo( |
|
() => hostUUID !== previousHostUUID, |
|
[hostUUID, previousHostUUID], |
|
); |
|
const isReloadHostDetail = useMemo( |
|
() => Boolean(hostUUID) && isDifferentHostUUID, |
|
[hostUUID, isDifferentHostUUID], |
|
); |
|
|
|
const panelHeaderElement = useMemo( |
|
() => ( |
|
<PanelHeader> |
|
<HeaderText>Prepare network on {hostDetail?.shortHostName}</HeaderText> |
|
</PanelHeader> |
|
), |
|
[hostDetail], |
|
); |
|
const netconfForm = useMemo( |
|
() => ( |
|
<NetworkInitForm |
|
expectHostDetail |
|
hostDetail={hostDetail} |
|
ref={netconfFormRef} |
|
toggleSubmitDisabled={(valid) => { |
|
setValidity(INPUT_GROUP_ID_PREP_NET_NETCONF, valid); |
|
}} |
|
/> |
|
), |
|
[hostDetail, setValidity], |
|
); |
|
const generalInputMessageArea = useMemo( |
|
() => ( |
|
<MessageGroup |
|
count={1} |
|
defaultMessageType="warning" |
|
ref={generalInputMessageGroupRef} |
|
/> |
|
), |
|
[], |
|
); |
|
|
|
const contentElement = useMemo(() => { |
|
let result; |
|
|
|
if (isLoadingHostDetail) { |
|
result = <Spinner mt={0} />; |
|
} else if (fatalErrorMessage) { |
|
result = <MessageBox {...fatalErrorMessage} />; |
|
} else { |
|
result = ( |
|
<> |
|
{panelHeaderElement} |
|
<FlexBox |
|
component="form" |
|
onSubmit={(...args) => { |
|
const [event] = args; |
|
|
|
event.preventDefault(); |
|
|
|
const body = getFormData( |
|
{ netconf: netconfFormRef.current }, |
|
...args, |
|
); |
|
|
|
setConfirmDialogProps({ |
|
actionProceedText: 'Prepare', |
|
content: ( |
|
<FormSummary |
|
entries={toFormEntries(body)} |
|
getEntryLabel={({ cap, key }) => |
|
/^(dns|[a-z]+n\d+)/.test(key) |
|
? key.toUpperCase() |
|
: cap(key) |
|
} |
|
/> |
|
), |
|
onProceedAppend: () => { |
|
submitForm({ |
|
body, |
|
getErrorMsg: (parentMsg) => ( |
|
<>Failed to prepare network. {parentMsg}</> |
|
), |
|
method: 'put', |
|
onSuccess: () => { |
|
setMapNetwork(0); |
|
}, |
|
setMsg: netconfFormRef?.current?.setMessage, |
|
successMsg: `Initiated prepare network on ${hostDetail?.shortHostName}`, |
|
url: `/host/${hostUUID}?handler=subnode-network`, |
|
}); |
|
}, |
|
titleText: `Prepare ${hostDetail?.shortHostName} network?`, |
|
}); |
|
|
|
confirmDialogRef.current.setOpen?.call(null, true); |
|
}} |
|
> |
|
<InputWithRef |
|
input={ |
|
<OutlinedInputWithLabel |
|
formControlProps={{ sx: { maxWidth: '20em' } }} |
|
id={INPUT_ID_PREP_NET_HOST_NAME} |
|
label={INPUT_LABEL_PREP_NET_HOST_NAME} |
|
value={hostDetail?.hostName} |
|
/> |
|
} |
|
inputTestBatch={buildPeacefulStringTestBatch( |
|
INPUT_LABEL_PREP_NET_HOST_NAME, |
|
() => { |
|
setMessage(INPUT_ID_PREP_NET_HOST_NAME); |
|
}, |
|
{ |
|
onFinishBatch: buildFinishInputTestBatchFunction( |
|
INPUT_ID_PREP_NET_HOST_NAME, |
|
), |
|
}, |
|
(message) => { |
|
setMessage(INPUT_ID_PREP_NET_HOST_NAME, { |
|
children: message, |
|
}); |
|
}, |
|
)} |
|
onFirstRender={buildInputFirstRenderFunction( |
|
INPUT_ID_PREP_NET_HOST_NAME, |
|
)} |
|
required |
|
/> |
|
{generalInputMessageArea} |
|
{netconfForm} |
|
<FlexBox row justifyContent="flex-end"> |
|
<ContainedButton disabled={isFormInvalid} type="submit"> |
|
Prepare network |
|
</ContainedButton> |
|
</FlexBox> |
|
</FlexBox> |
|
</> |
|
); |
|
} |
|
|
|
return result; |
|
}, [ |
|
isLoadingHostDetail, |
|
fatalErrorMessage, |
|
panelHeaderElement, |
|
hostDetail?.hostName, |
|
hostDetail?.shortHostName, |
|
buildFinishInputTestBatchFunction, |
|
buildInputFirstRenderFunction, |
|
generalInputMessageArea, |
|
netconfForm, |
|
isFormInvalid, |
|
setConfirmDialogProps, |
|
submitForm, |
|
hostUUID, |
|
setMessage, |
|
]); |
|
|
|
const getHostDetail = useCallback( |
|
(uuid: string) => { |
|
setIsLoadingHostDetail(true); |
|
|
|
if (isLoadingHostDetail) { |
|
api |
|
.get<APIHostDetail>(`/host/${uuid}`) |
|
.then(({ data }) => { |
|
setPreviousHostUUID(data.hostUUID); |
|
setHostDetail(data); |
|
}) |
|
.catch((error) => { |
|
const { children } = handleAPIError(error); |
|
|
|
setFatalErrorMessage({ |
|
children: `Failed to get target host information; cannot continue. ${children}`, |
|
type: 'error', |
|
}); |
|
}) |
|
.finally(() => { |
|
setIsLoadingHostDetail(false); |
|
}); |
|
} |
|
}, |
|
[ |
|
setIsLoadingHostDetail, |
|
isLoadingHostDetail, |
|
setPreviousHostUUID, |
|
setHostDetail, |
|
setFatalErrorMessage, |
|
], |
|
); |
|
|
|
useEffect(() => { |
|
if (isExpectExternalHostUUID) { |
|
if (isReloadHostDetail) { |
|
getHostDetail(hostUUID as string); |
|
} |
|
} else if (isReady && !fatalErrorMessage) { |
|
if (queryHostUUID) { |
|
getHostDetail(getQueryParam(queryHostUUID)); |
|
} else { |
|
setFatalErrorMessage({ |
|
children: `No host UUID provided; cannot continue.`, |
|
type: 'error', |
|
}); |
|
|
|
setIsLoadingHostDetail(false); |
|
} |
|
} |
|
}, [ |
|
fatalErrorMessage, |
|
getHostDetail, |
|
hostUUID, |
|
isExpectExternalHostUUID, |
|
isReady, |
|
queryHostUUID, |
|
setFatalErrorMessage, |
|
setHostDetail, |
|
setIsLoadingHostDetail, |
|
isReloadHostDetail, |
|
]); |
|
|
|
return ( |
|
<> |
|
<Panel>{contentElement}</Panel> |
|
<ConfirmDialog |
|
closeOnProceed |
|
scrollContent |
|
{...confirmDialogProps} |
|
ref={confirmDialogRef} |
|
/> |
|
</> |
|
); |
|
}; |
|
|
|
export default PrepareNetworkForm;
|
|
|