Skip to content

Commit 74172b0

Browse files
committed
CNV-52160: UDN with topology selector
1 parent 30ba9b2 commit 74172b0

File tree

8 files changed

+397
-72
lines changed

8 files changed

+397
-72
lines changed

Diff for: locales/en/plugin__networking-console-plugin.json

+3
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,16 @@
196196
"key is the label key that the selector applies": "key is the label key that the selector applies",
197197
"Kind": "Kind",
198198
"Labels": "Labels",
199+
"Layer 2": "Layer 2",
200+
"Layer 3": "Layer 3",
199201
"Layer2 topology creates one logical switch shared by all nodes.": "Layer2 topology creates one logical switch shared by all nodes.",
200202
"Layer3 topology creates a layer 2 segment per node, each with a different subnet. Layer 3 routing is used to interconnect node subnets.": "Layer3 topology creates a layer 2 segment per node, each with a different subnet. Layer 3 routing is used to interconnect node subnets.",
201203
"Learn how to use NetworkAttachmentDefinitions": "Learn how to use NetworkAttachmentDefinitions",
202204
"Learn more about {{ kind }}": "Learn more about {{ kind }}",
203205
"Learn more about working with projects": "Learn more about working with projects",
204206
"List of pods": "List of pods",
205207
"List of pods matching": "List of pods matching",
208+
"Localnet": "Localnet",
206209
"Location": "Location",
207210
"Location of the resource that backs the service": "Location of the resource that backs the service",
208211
"MAC spoof check": "MAC spoof check",

Diff for: src/utils/resources/udns/constants.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
export const PROJECT_LABEL_FOR_MATCH_EXPRESSION = 'kubernetes.io/metadata.name';
22

33
export const FIXED_PRIMARY_UDN_NAME = 'primary-udn';
4+
5+
export const LOCALNET_TOPOLOGY = 'Localnet';
6+
export const LAYER2_TOPOLOGY = 'Layer2';
7+
export const LAYER3_TOPOLOGY = 'Layer3';

Diff for: src/utils/resources/udns/types/types.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export type UserDefinedNetworkSubnet = string | UserDefinedNetworkLayer3Subnet;
2929
export type UserDefinedNetworkLayer3 = {
3030
joinSubnets?: string[];
3131
mtu?: number;
32-
role: string;
32+
role: UserDefinedNetworkRole;
3333
subnets?: UserDefinedNetworkLayer3Subnet[];
3434
};
3535

@@ -41,6 +41,11 @@ export type ClusterUserDefinedNetworkSpec = {
4141
export type UserDefinedNetworkSpec = {
4242
layer2?: UserDefinedNetworkLayer2;
4343
layer3?: UserDefinedNetworkLayer3;
44+
localnet?: {
45+
physicalNetworkName: string;
46+
role: UserDefinedNetworkRole;
47+
subnets?: UserDefinedNetworkSubnet[];
48+
};
4449
topology: string;
4550
};
4651

Diff for: src/views/udns/list/components/SubnetsInput.tsx

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React, { FC } from 'react';
2+
import { FieldPath, useFormContext, useWatch } from 'react-hook-form';
3+
4+
import { FormGroup, TextInput } from '@patternfly/react-core';
5+
import SubnetCIRDHelperText from '@utils/components/SubnetCIRDHelperText/SubnetCIRDHelperText';
6+
import { useNetworkingTranslation } from '@utils/hooks/useNetworkingTranslation';
7+
import {
8+
UserDefinedNetworkLayer3Subnet,
9+
UserDefinedNetworkSpec,
10+
} from '@utils/resources/udns/types';
11+
12+
import { UDNForm } from './constants';
13+
import { getSubnetFields, getSubnetsFromNetworkSpec } from './utils';
14+
15+
type SubnetsInputProps = {
16+
isClusterUDN?: boolean;
17+
};
18+
19+
const SubnetsInput: FC<SubnetsInputProps> = ({ isClusterUDN }) => {
20+
const { t } = useNetworkingTranslation();
21+
22+
const { setValue } = useFormContext<UDNForm>();
23+
const networkSpecPath = isClusterUDN ? 'spec.network' : 'spec';
24+
25+
const networkSpec: UserDefinedNetworkSpec = useWatch<UDNForm>({
26+
name: networkSpecPath,
27+
});
28+
29+
const subnets = getSubnetsFromNetworkSpec(networkSpec);
30+
31+
const subnetsText = networkSpec.layer3
32+
? (subnets as UserDefinedNetworkLayer3Subnet[]).map((subnet) => subnet.cidr).join(',')
33+
: subnets?.join(',');
34+
35+
return (
36+
<FormGroup fieldId="input-udn-subnet" isRequired label={t('Subnet CIRD')}>
37+
<TextInput
38+
autoFocus
39+
data-test="input-udn-subnet"
40+
id="input-udn-subnet"
41+
isRequired
42+
name="input-udn-subnet"
43+
onChange={(_, newValue) => {
44+
const subnetField = getSubnetFields(networkSpec, isClusterUDN);
45+
46+
const newSubnet = networkSpec.layer3
47+
? newValue.split(',').map((subnet) => ({ cidr: subnet }))
48+
: newValue.split(',');
49+
50+
setValue(subnetField as FieldPath<UDNForm>, newSubnet, {
51+
shouldValidate: true,
52+
});
53+
}}
54+
type="text"
55+
value={subnetsText}
56+
/>
57+
<SubnetCIRDHelperText />
58+
</FormGroup>
59+
);
60+
};
61+
62+
export default SubnetsInput;

Diff for: src/views/udns/list/components/Topology.tsx

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import React, { FC, Ref, useState } from 'react';
2+
import { useFormContext, useWatch } from 'react-hook-form';
3+
4+
import {
5+
DropdownItem,
6+
Flex,
7+
FlexItem,
8+
FormGroup,
9+
MenuToggle,
10+
MenuToggleElement,
11+
Radio,
12+
Select,
13+
TextInput,
14+
} from '@patternfly/react-core';
15+
import FormGroupHelperText from '@utils/components/FormGroupHelperText/FormGroupHelperText';
16+
import { useNetworkingTranslation } from '@utils/hooks/useNetworkingTranslation';
17+
import { LOCALNET_TOPOLOGY } from '@utils/resources/udns/constants';
18+
import { UserDefinedNetworkRole, UserDefinedNetworkSpec } from '@utils/resources/udns/types';
19+
20+
import { UDNForm } from './constants';
21+
import { createNetworkSpecFromRole, createNetworkSpecFromTopology, getTopology } from './utils';
22+
23+
type TopologyProps = {
24+
isClusterUDN: boolean;
25+
};
26+
27+
const Topology: FC<TopologyProps> = ({ isClusterUDN }) => {
28+
const { t } = useNetworkingTranslation();
29+
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
30+
const { register, setValue } = useFormContext<UDNForm>();
31+
32+
const networkSpecPath = isClusterUDN ? 'spec.network' : 'spec';
33+
34+
const networkSpec: UserDefinedNetworkSpec = useWatch<UDNForm>({
35+
name: networkSpecPath,
36+
});
37+
38+
const topology = getTopology(networkSpec);
39+
40+
const isPrimary =
41+
networkSpec?.layer2?.role === UserDefinedNetworkRole.Primary ||
42+
networkSpec?.layer3?.role === UserDefinedNetworkRole.Primary;
43+
44+
const onChangeRole = (role: UserDefinedNetworkRole) => {
45+
setValue(networkSpecPath, createNetworkSpecFromRole(networkSpec, role));
46+
};
47+
48+
return (
49+
<>
50+
<FormGroup>
51+
<Flex>
52+
<FlexItem>
53+
<Radio
54+
id="primary-topology"
55+
isChecked={isPrimary}
56+
label="Primary"
57+
name="radio-topology"
58+
onChange={() => onChangeRole(UserDefinedNetworkRole.Primary)}
59+
></Radio>
60+
</FlexItem>
61+
<FlexItem>
62+
<Radio
63+
id="secondary-topology"
64+
isChecked={!isPrimary}
65+
label="Secondary"
66+
name="radio-topology"
67+
onChange={() => onChangeRole(UserDefinedNetworkRole.Secondary)}
68+
></Radio>
69+
</FlexItem>
70+
</Flex>
71+
</FormGroup>
72+
<FormGroup fieldId="select-topology" isRequired label={t('Topology')}>
73+
<Select
74+
id="select-topology"
75+
isOpen={isDropdownOpen}
76+
onOpenChange={setIsDropdownOpen}
77+
onSelect={(_, selection) => {
78+
setValue(
79+
isClusterUDN ? 'spec.network' : 'spec',
80+
createNetworkSpecFromTopology(selection as string, networkSpec),
81+
);
82+
setIsDropdownOpen(false);
83+
}}
84+
selected={topology}
85+
toggle={(toggleRef: Ref<MenuToggleElement>) => (
86+
<MenuToggle
87+
id="toggle-udns-operator"
88+
isDisabled={!isPrimary}
89+
isExpanded={isDropdownOpen}
90+
isFullWidth
91+
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
92+
ref={toggleRef}
93+
>
94+
{topology}
95+
</MenuToggle>
96+
)}
97+
>
98+
<>
99+
{isPrimary ? (
100+
<>
101+
<DropdownItem value="Layer2">{t('Layer 2')}</DropdownItem>
102+
<DropdownItem value="Layer3">{t('Layer 3')}</DropdownItem>
103+
</>
104+
) : (
105+
<DropdownItem value="Locanet">{t('Localnet')}</DropdownItem>
106+
)}
107+
</>
108+
</Select>
109+
</FormGroup>
110+
111+
{topology === LOCALNET_TOPOLOGY && (
112+
<FormGroup fieldId="input-name" isRequired label={t('Physical network name')}>
113+
<TextInput
114+
autoFocus
115+
{...register('spec.localnet.physicalNetworkName', { required: true })}
116+
isRequired
117+
/>
118+
119+
<FormGroupHelperText>
120+
{t(
121+
'The name of the physical network. This attribute must match the value of the spec.desiredState.ovn.bridge-mappings.localnet field of the NodeNetworkConfigurationPolicy object that defines the OVS bridge mapping. ',
122+
)}
123+
</FormGroupHelperText>
124+
</FormGroup>
125+
)}
126+
</>
127+
);
128+
};
129+
130+
export default Topology;

Diff for: src/views/udns/list/components/UserDefinedNetworkCreateForm.tsx

+7-39
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
import React, { FC, FormEventHandler } from 'react';
2-
import { Controller, useFormContext } from 'react-hook-form';
2+
import { useFormContext } from 'react-hook-form';
33

4-
import { Alert, AlertVariant, Content, Form, FormGroup, TextInput } from '@patternfly/react-core';
5-
import SubnetCIRDHelperText from '@utils/components/SubnetCIRDHelperText/SubnetCIRDHelperText';
4+
import { Content, Form, FormGroup, TextInput } from '@patternfly/react-core';
65
import { useNetworkingTranslation } from '@utils/hooks/useNetworkingTranslation';
76

87
import ClusterUDNNamespaceSelector from './ClusterUDNNamespaceSelector';
98
import { UDNForm } from './constants';
109
import SelectProject from './SelectProject';
10+
import SubnetsInput from './SubnetsInput';
11+
import Topology from './Topology';
1112

1213
type UserDefinedNetworkCreateFormProps = {
13-
error: Error;
1414
isClusterUDN?: boolean;
1515
onSubmit: FormEventHandler<HTMLFormElement>;
1616
};
1717

1818
const UserDefinedNetworkCreateForm: FC<UserDefinedNetworkCreateFormProps> = ({
19-
error,
2019
isClusterUDN,
2120
onSubmit,
2221
}) => {
2322
const { t } = useNetworkingTranslation();
2423

25-
const { control, register, setValue } = useFormContext<UDNForm>();
26-
27-
const subnetField = isClusterUDN ? 'spec.network.layer2.subnets' : 'spec.layer2.subnets';
24+
const { register } = useFormContext<UDNForm>();
2825

2926
return (
3027
<Form id="create-udn-form" onSubmit={onSubmit}>
@@ -48,39 +45,10 @@ const UserDefinedNetworkCreateForm: FC<UserDefinedNetworkCreateFormProps> = ({
4845
</FormGroup>
4946
)}
5047

51-
<FormGroup fieldId="input-udn-subnet" isRequired label={t('Subnet CIRD')}>
52-
<Controller
53-
control={control}
54-
name={subnetField}
55-
render={({ field: { value } }) => (
56-
<TextInput
57-
autoFocus
58-
data-test="input-udn-subnet"
59-
id="input-udn-subnet"
60-
isRequired
61-
name="input-udn-subnet"
62-
onChange={(_, newValue) =>
63-
setValue(subnetField, newValue.split(','), {
64-
shouldValidate: true,
65-
})
66-
}
67-
type="text"
68-
value={value?.join(',')}
69-
/>
70-
)}
71-
rules={{ required: true }}
72-
/>
73-
74-
<SubnetCIRDHelperText />
75-
</FormGroup>
48+
<SubnetsInput isClusterUDN={isClusterUDN} />
7649

50+
<Topology isClusterUDN={isClusterUDN} />
7751
{isClusterUDN && <ClusterUDNNamespaceSelector />}
78-
79-
{error && (
80-
<Alert isInline title={t('Error')} variant={AlertVariant.danger}>
81-
{error?.message}
82-
</Alert>
83-
)}
8452
</Form>
8553
);
8654
};

0 commit comments

Comments
 (0)