fix(striker-ui): repopulate network configs in network form

main
Tsu-ba-me 1 year ago
parent 6874218e3c
commit 5f5d521c58
  1. 274
      striker-ui/components/NetworkInitForm.tsx
  2. 51
      striker-ui/components/PrepareNetworkForm.tsx
  3. 2
      striker-ui/lib/consts/NETWORK_TYPES.ts
  4. 19
      striker-ui/types/APIHost.d.ts
  5. 1
      striker-ui/types/NetworkType.d.ts
  6. 1
      striker-ui/types/NumberBoolean.d.ts

@ -577,17 +577,33 @@ NetworkForm.defaultProps = {
const NetworkInitForm = forwardRef< const NetworkInitForm = forwardRef<
NetworkInitFormForwardedRefContent, NetworkInitFormForwardedRefContent,
{ {
expectHostDetail?: boolean;
hostDetail?: APIHostDetail; hostDetail?: APIHostDetail;
toggleSubmitDisabled?: (testResult: boolean) => void; toggleSubmitDisabled?: (testResult: boolean) => void;
} }
>(({ hostDetail, toggleSubmitDisabled }, ref) => { >(({ expectHostDetail = false, hostDetail, toggleSubmitDisabled }, ref) => {
const { const {
dns: xDns, dns: previousDns,
gateway: xGateway, gateway: previousGateway,
hostType, hostType,
hostUUID = 'local', hostUUID = 'local',
networks: previousNetworks,
}: APIHostDetail = hostDetail ?? ({} as APIHostDetail); }: APIHostDetail = hostDetail ?? ({} as APIHostDetail);
const uninitRequiredNetworks: NetworkInput[] = useMemo(
() =>
hostType === 'node' ? NODE_REQUIRED_NETWORKS : STRIKER_REQUIRED_NETWORKS,
[hostType],
);
const requiredNetworks = useMemo<Partial<Record<NetworkType, number>>>(
() =>
hostType === 'node' ? { bcn: 1, ifn: 1, sn: 1 } : { bcn: 1, ifn: 1 },
[hostType],
);
const [isReadHostDetail, setIsReadHostDetail] = useState<boolean>(false);
const [dragMousePosition, setDragMousePosition] = useState<{ const [dragMousePosition, setDragMousePosition] = useState<{
x: number; x: number;
y: number; y: number;
@ -595,7 +611,7 @@ const NetworkInitForm = forwardRef<
const [networkInterfaceInputMap, setNetworkInterfaceInputMap] = const [networkInterfaceInputMap, setNetworkInterfaceInputMap] =
useState<NetworkInterfaceInputMap>({}); useState<NetworkInterfaceInputMap>({});
const [networkInputs, setNetworkInputs] = useState<NetworkInput[]>( const [networkInputs, setNetworkInputs] = useState<NetworkInput[]>(
hostType === 'node' ? NODE_REQUIRED_NETWORKS : STRIKER_REQUIRED_NETWORKS, uninitRequiredNetworks,
); );
const [networkInterfaceHeld, setNetworkInterfaceHeld] = useState< const [networkInterfaceHeld, setNetworkInterfaceHeld] = useState<
NetworkInterfaceOverviewMetadata | undefined NetworkInterfaceOverviewMetadata | undefined
@ -606,24 +622,31 @@ const NetworkInitForm = forwardRef<
const dnsCSVInputRef = useRef<InputForwardedRefContent<'string'>>({}); const dnsCSVInputRef = useRef<InputForwardedRefContent<'string'>>({});
const messageGroupRef = useRef<MessageGroupForwardedRefContent>({}); const messageGroupRef = useRef<MessageGroupForwardedRefContent>({});
const { data: networkInterfaces = [], isLoading } = periodicFetch< const {
NetworkInterfaceOverviewMetadata[] data: networkInterfaces = [],
>(`${API_BASE_URL}/init/network-interface/${hostUUID}`, { isLoading: isLoadingNetworkInterfaces,
refreshInterval: 2000, } = periodicFetch<NetworkInterfaceOverviewMetadata[]>(
onSuccess: (data) => { `${API_BASE_URL}/init/network-interface/${hostUUID}`,
const map = data.reduce<NetworkInterfaceInputMap>((result, metadata) => { {
const { networkInterfaceUUID } = metadata; refreshInterval: 2000,
onSuccess: (data) => {
result[networkInterfaceUUID] = networkInterfaceInputMap[ const map = data.reduce<NetworkInterfaceInputMap>(
networkInterfaceUUID (result, metadata) => {
] ?? { metadata }; const { networkInterfaceUUID } = metadata;
return result; result[networkInterfaceUUID] = networkInterfaceInputMap[
}, {}); networkInterfaceUUID
] ?? { metadata };
return result;
},
{},
);
setNetworkInterfaceInputMap(map); setNetworkInterfaceInputMap(map);
},
}, },
}); );
const isDisableAddNetworkButton: boolean = useMemo( const isDisableAddNetworkButton: boolean = useMemo(
() => () =>
@ -634,6 +657,10 @@ const NetworkInitForm = forwardRef<
(hostType === 'node' && networkInterfaces.length <= 6), (hostType === 'node' && networkInterfaces.length <= 6),
[hostType, networkInputs, networkInterfaces, networkInterfaceInputMap], [hostType, networkInputs, networkInterfaces, networkInterfaceInputMap],
); );
const isLoadingHostDetail: boolean = useMemo(
() => expectHostDetail && !hostDetail,
[expectHostDetail, hostDetail],
);
const setMessage = useCallback( const setMessage = useCallback(
(key: string, message?: Message) => (key: string, message?: Message) =>
@ -986,20 +1013,31 @@ const NetworkInitForm = forwardRef<
const clearNetworkInterfaceHeld = useCallback(() => { const clearNetworkInterfaceHeld = useCallback(() => {
setNetworkInterfaceHeld(undefined); setNetworkInterfaceHeld(undefined);
}, []); }, []);
const createNetwork = useCallback(() => { const createNetwork = useCallback(
networkInputs.unshift({ ({
inputUUID: uuidv4(), inputUUID = uuidv4(),
interfaces: [...INITIAL_IFACES], interfaces = [...INITIAL_IFACES],
ipAddress: '', ipAddress = '',
name: 'Unknown Network', name = 'Unknown Network',
subnetMask: '', subnetMask = '',
type: '', type = '',
typeCount: 0, typeCount = 0,
}); }: Partial<NetworkInput> = {}) => {
networkInputs.unshift({
toggleSubmitDisabled?.call(null, false); inputUUID,
setNetworkInputs([...networkInputs]); interfaces,
}, [networkInputs, toggleSubmitDisabled]); ipAddress,
name,
subnetMask,
type,
typeCount,
});
toggleSubmitDisabled?.call(null, false);
setNetworkInputs([...networkInputs]);
},
[networkInputs, toggleSubmitDisabled],
);
const removeNetwork = useCallback( const removeNetwork = useCallback(
(networkIndex: number) => { (networkIndex: number) => {
const [{ inputUUID, interfaces }] = networkInputs.splice(networkIndex, 1); const [{ inputUUID, interfaces }] = networkInputs.splice(networkIndex, 1);
@ -1138,6 +1176,55 @@ const NetworkInitForm = forwardRef<
[clearNetworkInterfaceHeld, networkInterfaceHeld], [clearNetworkInterfaceHeld, networkInterfaceHeld],
); );
useEffect(() => {
if (
Object.keys(networkInterfaceInputMap).length > 0 &&
expectHostDetail &&
hostDetail &&
!isReadHostDetail
) {
setNetworkInputs(
Object.values(previousNetworks).reduce<NetworkInput[]>(
(previous, { ip, link1Uuid, link2Uuid = '', subnetMask, type }) => {
const name = NETWORK_TYPES[type];
const typeCount =
getNetworkTypeCount(type, { inputs: previous }) + 1;
const isRequired = requiredNetworks[type] === typeCount;
previous.push({
inputUUID: uuidv4(),
interfaces: [
networkInterfaceInputMap[link1Uuid]?.metadata,
networkInterfaceInputMap[link2Uuid]?.metadata,
],
ipAddress: ip,
isRequired,
name,
subnetMask,
type,
typeCount,
});
return previous;
},
[],
),
);
setIsReadHostDetail(true);
}
}, [
createNetwork,
expectHostDetail,
getNetworkTypeCount,
hostDetail,
isReadHostDetail,
networkInputs,
networkInterfaceInputMap,
previousNetworks,
requiredNetworks,
]);
useEffect(() => { useEffect(() => {
// Enable network mapping on component mount. // Enable network mapping on component mount.
setMapNetwork(1); setMapNetwork(1);
@ -1196,7 +1283,7 @@ const NetworkInitForm = forwardRef<
const networkInputMinWidth = '13em'; const networkInputMinWidth = '13em';
const networkInputWidth = '25%'; const networkInputWidth = '25%';
return isLoading ? ( return isLoadingNetworkInterfaces ? (
<Spinner /> <Spinner />
) : ( ) : (
<MUIBox <MUIBox
@ -1288,65 +1375,67 @@ const NetworkInitForm = forwardRef<
}, },
}} }}
/> />
<FlexBox {!isLoadingHostDetail && (
row <FlexBox
sx={{ row
'& > :first-child': {
alignSelf: 'start',
marginTop: '.7em',
},
'& > :last-child': {
flexGrow: 1,
},
}}
>
<MUIBox
sx={{ sx={{
alignItems: 'strech', '& > :first-child': {
display: 'flex', alignSelf: 'start',
flexDirection: 'row', marginTop: '.7em',
overflowX: 'auto',
paddingLeft: '.3em',
'& > div': {
marginBottom: '.8em',
marginTop: '.4em',
minWidth: networkInputMinWidth,
width: networkInputWidth,
}, },
'& > :not(:first-child)': { '& > :last-child': {
marginLeft: '1em', flexGrow: 1,
}, },
}} }}
> >
{networkInputs.map((networkInput, networkIndex) => { <MUIBox
const { inputUUID } = networkInput; sx={{
alignItems: 'strech',
return ( display: 'flex',
<NetworkForm flexDirection: 'row',
key={`network-${inputUUID}`} overflowX: 'auto',
{...{ paddingLeft: '.3em',
createDropMouseUpHandler,
getNetworkTypeCount, '& > div': {
hostDetail, marginBottom: '.8em',
networkIndex, marginTop: '.4em',
networkInput, minWidth: networkInputMinWidth,
networkInterfaceCount: networkInterfaces.length, width: networkInputWidth,
networkInterfaceInputMap, },
removeNetwork,
setMessageRe, '& > :not(:first-child)': {
setNetworkInputs, marginLeft: '1em',
setNetworkInterfaceInputMap, },
testInput, }}
testInputToToggleSubmitDisabled, >
}} {networkInputs.map((networkInput, networkIndex) => {
/> const { inputUUID } = networkInput;
);
})} return (
</MUIBox> <NetworkForm
</FlexBox> key={`network-${inputUUID}`}
{...{
createDropMouseUpHandler,
getNetworkTypeCount,
hostDetail,
networkIndex,
networkInput,
networkInterfaceCount: networkInterfaces.length,
networkInterfaceInputMap,
removeNetwork,
setMessageRe,
setNetworkInputs,
setNetworkInterfaceInputMap,
testInput,
testInputToToggleSubmitDisabled,
}}
/>
);
})}
</MUIBox>
</FlexBox>
)}
<FlexBox <FlexBox
sm="row" sm="row"
sx={{ sx={{
@ -1360,7 +1449,9 @@ const NetworkInitForm = forwardRef<
> >
<IconButton <IconButton
disabled={isDisableAddNetworkButton} disabled={isDisableAddNetworkButton}
onClick={createNetwork} onClick={() => {
createNetwork();
}}
> >
<MUIAddIcon /> <MUIAddIcon />
</IconButton> </IconButton>
@ -1381,7 +1472,7 @@ const NetworkInitForm = forwardRef<
setGatewayInputMessage(); setGatewayInputMessage();
}} }}
label="Gateway" label="Gateway"
value={xGateway} value={previousGateway}
/> />
} }
ref={gatewayInputRef} ref={gatewayInputRef}
@ -1403,7 +1494,7 @@ const NetworkInitForm = forwardRef<
setDnsInputMessage(); setDnsInputMessage();
}} }}
label="Domain name server(s)" label="Domain name server(s)"
value={xDns} value={previousDns}
/> />
} }
ref={dnsCSVInputRef} ref={dnsCSVInputRef}
@ -1420,6 +1511,7 @@ const NetworkInitForm = forwardRef<
}); });
NetworkInitForm.defaultProps = { NetworkInitForm.defaultProps = {
expectHostDetail: false,
hostDetail: undefined, hostDetail: undefined,
toggleSubmitDisabled: undefined, toggleSubmitDisabled: undefined,
}; };

@ -13,30 +13,27 @@ import OutlinedInputWithLabel from './OutlinedInputWithLabel';
import { Panel, PanelHeader } from './Panels'; import { Panel, PanelHeader } from './Panels';
import Spinner from './Spinner'; import Spinner from './Spinner';
import { HeaderText } from './Text'; import { HeaderText } from './Text';
import useProtect from '../hooks/useProtect';
import useProtectedState from '../hooks/useProtectedState'; import useProtectedState from '../hooks/useProtectedState';
const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({ const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({
expectUUID: isExpectExternalHostUUID = false, expectUUID: isExpectExternalHostUUID = false,
hostUUID, hostUUID,
}) => { }) => {
const { protect } = useProtect();
const { const {
isReady, isReady,
query: { host_uuid: queryHostUUID }, query: { host_uuid: queryHostUUID },
} = useRouter(); } = useRouter();
const [dataHostDetail, setDataHostDetail] = useProtectedState< const [hostDetail, setHostDetail] = useProtectedState<
APIHostDetail | undefined APIHostDetail | undefined
>(undefined, protect); >(undefined);
const [fatalErrorMessage, setFatalErrorMessage] = useProtectedState< const [fatalErrorMessage, setFatalErrorMessage] = useProtectedState<
Message | undefined Message | undefined
>(undefined, protect); >(undefined);
const [isLoading, setIsLoading] = useProtectedState<boolean>(true, protect); const [isLoadingHostDetail, setIsLoadingHostDetail] =
const [previousHostUUID, setPreviousHostUUID] = useProtectedState< useProtectedState<boolean>(true);
PrepareNetworkFormProps['hostUUID'] const [previousHostUUID, setPreviousHostUUID] =
>(undefined, protect); useProtectedState<PrepareNetworkFormProps['hostUUID']>(undefined);
const isDifferentHostUUID = useMemo( const isDifferentHostUUID = useMemo(
() => hostUUID !== previousHostUUID, () => hostUUID !== previousHostUUID,
@ -50,17 +47,15 @@ const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({
const panelHeaderElement = useMemo( const panelHeaderElement = useMemo(
() => ( () => (
<PanelHeader> <PanelHeader>
<HeaderText> <HeaderText>Prepare network on {hostDetail?.shortHostName}</HeaderText>
Prepare network on {dataHostDetail?.shortHostName}
</HeaderText>
</PanelHeader> </PanelHeader>
), ),
[dataHostDetail], [hostDetail],
); );
const contentElement = useMemo(() => { const contentElement = useMemo(() => {
let result; let result;
if (isLoading) { if (isLoadingHostDetail) {
result = <Spinner mt={0} />; result = <Spinner mt={0} />;
} else if (fatalErrorMessage) { } else if (fatalErrorMessage) {
result = <MessageBox {...fatalErrorMessage} />; result = <MessageBox {...fatalErrorMessage} />;
@ -75,12 +70,12 @@ const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({
formControlProps={{ sx: { maxWidth: '20em' } }} formControlProps={{ sx: { maxWidth: '20em' } }}
id="prepare-network-host-name" id="prepare-network-host-name"
label="Host name" label="Host name"
value={dataHostDetail?.hostName} value={hostDetail?.hostName}
/> />
} }
required required
/> />
<NetworkInitForm hostDetail={dataHostDetail} /> <NetworkInitForm expectHostDetail hostDetail={hostDetail} />
<FlexBox row justifyContent="flex-end"> <FlexBox row justifyContent="flex-end">
<ContainedButton>Prepare network</ContainedButton> <ContainedButton>Prepare network</ContainedButton>
</FlexBox> </FlexBox>
@ -90,18 +85,18 @@ const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({
} }
return result; return result;
}, [dataHostDetail, fatalErrorMessage, isLoading, panelHeaderElement]); }, [hostDetail, fatalErrorMessage, isLoadingHostDetail, panelHeaderElement]);
const getHostDetail = useCallback( const getHostDetail = useCallback(
(uuid: string) => { (uuid: string) => {
setIsLoading(true); setIsLoadingHostDetail(true);
if (isLoading) { if (isLoadingHostDetail) {
api api
.get<APIHostDetail>(`/host/${uuid}`) .get<APIHostDetail>(`/host/${uuid}`)
.then(({ data }) => { .then(({ data }) => {
setPreviousHostUUID(data.hostUUID); setPreviousHostUUID(data.hostUUID);
setDataHostDetail(data); setHostDetail(data);
}) })
.catch((error) => { .catch((error) => {
const { children } = handleAPIError(error); const { children } = handleAPIError(error);
@ -112,15 +107,15 @@ const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({
}); });
}) })
.finally(() => { .finally(() => {
setIsLoading(false); setIsLoadingHostDetail(false);
}); });
} }
}, },
[ [
setIsLoading, setIsLoadingHostDetail,
isLoading, isLoadingHostDetail,
setPreviousHostUUID, setPreviousHostUUID,
setDataHostDetail, setHostDetail,
setFatalErrorMessage, setFatalErrorMessage,
], ],
); );
@ -139,7 +134,7 @@ const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({
type: 'error', type: 'error',
}); });
setIsLoading(false); setIsLoadingHostDetail(false);
} }
} }
}, [ }, [
@ -150,8 +145,8 @@ const PrepareNetworkForm: FC<PrepareNetworkFormProps> = ({
isReady, isReady,
queryHostUUID, queryHostUUID,
setFatalErrorMessage, setFatalErrorMessage,
setDataHostDetail, setHostDetail,
setIsLoading, setIsLoadingHostDetail,
isReloadHostDetail, isReloadHostDetail,
]); ]);

@ -1,4 +1,4 @@
const NETWORK_TYPES: Record<string, string> = { const NETWORK_TYPES: Record<NetworkType, string> & Record<string, string> = {
bcn: 'Back-Channel Network', bcn: 'Back-Channel Network',
ifn: 'Internet-Facing Network', ifn: 'Internet-Facing Network',
mn: 'Migration Network', mn: 'Migration Network',

@ -41,8 +41,27 @@ type APIHostOverviewList = {
type APIHostDetail = APIHostOverview & { type APIHostDetail = APIHostOverview & {
dns: string; dns: string;
domain?: string;
gateway: string; gateway: string;
gatewayInterface: string;
installTarget: APIHostInstallTarget; installTarget: APIHostInstallTarget;
networks: {
[networkId: string]: {
createBridge?: NumberBoolean;
ip: string;
link1MacToSet: string;
link1Uuid: string;
link2MacToSet?: string;
link2Uuid?: string;
subnetMask: string;
type: NetworkType;
};
};
organization?: string;
prefix?: string;
sequence?: string;
strikerPassword?: string;
strikerUser?: string;
}; };
type APIDeleteHostConnectionRequestBody = { [key: 'local' | string]: string[] }; type APIDeleteHostConnectionRequestBody = { [key: 'local' | string]: string[] };

@ -0,0 +1 @@
type NetworkType = 'bcn' | 'ifn' | 'mn' | 'sn';

@ -0,0 +1 @@
type NumberBoolean = '0' | '1';
Loading…
Cancel
Save