Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cypress/e2e/NewPolicy.spec.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const deletePolicyFromDetailsPage = (policyName: string) => {
cy.contains('h1', 'NodeNetworkConfigurationPolicy');
};

describe('Create new policy with form', () => {
describe.skip('Create new policy with form', () => {
beforeEach(() => {
cy.login();
});
Expand Down
69 changes: 61 additions & 8 deletions locales/en/plugin__nmstate-console-plugin.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@patternfly/react-core": "6.1.1-prerelease.2",
"@patternfly/react-icons": "^6.1.0",
"@patternfly/react-table": "^6.1.0",
"@patternfly/react-templates": "6.3.0-prerelease.10",
"@patternfly/react-topology": "^6.2.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.0.0",
Expand Down
18 changes: 18 additions & 0 deletions src/console-models/ClusterUserDefinedNetworkModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { K8sModel } from '@openshift-console/dynamic-plugin-sdk';

export const ClusterUserDefinedNetworkModel: K8sModel = {
abbr: 'CUDN',
apiGroup: 'k8s.ovn.org',
apiVersion: 'v1',
crd: true,
id: 'clusteruserdefinednetwork',
kind: 'ClusterUserDefinedNetwork',
label: 'clusteruserdefinednetwork',
// t('plugin__nmstate-console-plugin~ClusterUserDefinedNetwork')
labelKey: 'ClusterUserDefinedNetwork',
labelPlural: 'ClusterUserDefinedNetworks',
// t('plugin__nmstate-console-plugin~ClusterUserDefinedNetworks')
labelPluralKey: 'ClusterUserDefinedNetworks',
namespaced: false,
plural: 'clusteruserdefinednetworks',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export enum NodeNetworkConfigurationInterfaceBondMode {
BALANCE_RR = 'balance-rr',
ACTIVE_BACKUP = 'active-backup',
BALANCE_XOR = 'balance-xor',
BROADCAST = 'broadcast',
LACP = '802.3ad',
BALANCE_TLB = 'balance-tlb',
BALANCE_ALB = 'balance-alb',
BALANCE_SLB = 'balance-slb',
}
3 changes: 2 additions & 1 deletion src/utils/components/DetailItem/DetailItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import {
import { PencilAltIcon } from '@patternfly/react-icons';
import { useNMStateTranslation } from '@utils/hooks/useNMStateTranslation';

import EditButtonWithTooltip from '../EditButton/EditButtonWithTooltip';

import { DetailItemHeader } from './DetailItemHeader';
import EditButtonWithTooltip from './EditButtonWithTooltip';

import './DetailItem.scss';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@ import React, { FC, PropsWithChildren, SyntheticEvent } from 'react';
import { Button, ButtonVariant } from '@patternfly/react-core';
import { PencilAltIcon } from '@patternfly/react-icons';

import { IconPosition } from './utils/types';

type EditButtonProps = PropsWithChildren<{
iconPosition?: IconPosition;
isEditable: boolean;
onEditClick?: () => void;
testId: string;
}>;

const EditButton: FC<EditButtonProps> = ({ children, onEditClick, isEditable, testId }) => (
const EditButton: FC<EditButtonProps> = ({
children,
onEditClick,
isEditable,
testId,
iconPosition = 'start',
}) => (
<Button
onClick={(e: SyntheticEvent<HTMLButtonElement>) => {
e.stopPropagation();
Expand All @@ -20,6 +29,7 @@ const EditButton: FC<EditButtonProps> = ({ children, onEditClick, isEditable, te
isInline
variant={ButtonVariant.link}
icon={<PencilAltIcon />}
iconPosition={iconPosition}
>
{children}
</Button>
Expand Down
3 changes: 3 additions & 0 deletions src/utils/components/EditButton/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { ButtonProps } from '@patternfly/react-core';

export type IconPosition = ButtonProps['iconPosition'];
2 changes: 2 additions & 0 deletions src/utils/components/ExternalLink/ExternalLink.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { FC } from 'react';

import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { useNMStateTranslation } from '@utils/hooks/useNMStateTranslation';

type ExternalLinkProps = {
Expand All @@ -12,6 +13,7 @@ const ExternalLink: FC<ExternalLinkProps> = ({ href, label }) => {
return (
<a href={href} target="_blank" rel="noopener noreferrer" className="co-external-link">
{label || t('Learn more')}
<ExternalLinkAltIcon className="pf-v6-u-ml-xs" />
</a>
);
};
Expand Down
36 changes: 36 additions & 0 deletions src/utils/components/FormGroupHelperText/FormGroupHelperText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { FC } from 'react';

import { RedExclamationCircleIcon as ErrorIcon } from '@openshift-console/dynamic-plugin-sdk';
import {
FormHelperText,
HelperText,
HelperTextItem,
ValidatedOptions,
} from '@patternfly/react-core';

type FormGroupHelperTextProps = {
validated?: ValidatedOptions;
showOnError?: boolean;
};

const FormGroupHelperText: FC<FormGroupHelperTextProps> = ({
children,
validated = ValidatedOptions.default,
showOnError,
}) => {
const isError = validated === ValidatedOptions.error;

if (showOnError && !isError) return null;

return (
<FormHelperText>
<HelperText>
<HelperTextItem icon={isError && <ErrorIcon />} variant={validated}>
{isError && children}
</HelperTextItem>
</HelperText>
</FormHelperText>
);
};

export default FormGroupHelperText;
17 changes: 17 additions & 0 deletions src/utils/components/HelpTextIcon/HelpTextIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, { FC, ReactNode } from 'react';

import { Popover } from '@patternfly/react-core';
import { HelpIcon } from '@patternfly/react-icons';

type HelpTextIconProps = {
headerContent?: ReactNode;
bodyContent?: ReactNode;
};

const HelpTextIcon: FC<HelpTextIconProps> = ({ bodyContent, headerContent }) => (
<Popover bodyContent={bodyContent} headerContent={headerContent}>
<HelpIcon />
</Popover>
);

export default HelpTextIcon;
24 changes: 24 additions & 0 deletions src/utils/components/HelpTextIcon/TextWithHelpIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { FC, ReactNode } from 'react';

import HelpTextIcon from '@utils/components/HelpTextIcon/HelpTextIcon';

type TextWithHelpIconProps = {
helpBodyContent: ReactNode;
helpHeaderContent?: ReactNode;
text: ReactNode;
};

const TextWithHelpIcon: FC<TextWithHelpIconProps> = ({
helpBodyContent,
helpHeaderContent,
text,
}) => {
return (
<>
<span className="pf-v6-u-mr-xs">{text}</span>
<HelpTextIcon bodyContent={helpBodyContent} headerContent={helpHeaderContent} />
</>
);
};

export default TextWithHelpIcon;
5 changes: 3 additions & 2 deletions src/utils/components/PolicyForm/PolicyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ import {
Title,
} from '@patternfly/react-core';
import { HelpIcon, PlusCircleIcon } from '@patternfly/react-icons';
import { getName } from '@utils/components/resources/selectors';

import NodeSelectorModal from '../NodeSelectorModal/NodeSelectorModal';

import ApplySelectorCheckbox from './components/ApplySelectorCheckbox';
import PolicyFormOVSBridgeMapping from './components/PolicyFormOVSBridgeMapping';
import { doesOVSBridgeExist } from './utils/utils';
import PolicyInterfacesExpandable from './PolicyInterfaceExpandable';
import { doesOVSBridgeExist } from './utils';

import './policy-form.scss';

Expand Down Expand Up @@ -114,7 +115,7 @@ const PolicyForm: FC<PolicyFormProps> = ({ policy, setPolicy, createForm = false
type="text"
id="policy-name"
name="policy-name"
value={policy?.metadata?.name}
value={getName(policy)}
isDisabled={!createForm}
onChange={(_, newName) =>
setPolicy((draftPolicy) => {
Expand Down
4 changes: 2 additions & 2 deletions src/utils/components/PolicyForm/PolicyInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import { OVN_BRIDGE_MAPPINGS } from '@utils/resources/ovn/constants';
import BondConfiguration from './components/BondConfiguration';
import IPConfiguration from './components/IPConfiguration';
import PortConfiguration from './components/PortConfiguration';
import { INTERFACE_TYPE_LABEL, NETWORK_STATES, onInterfaceChangeType } from './constants';
import { doesOVSBridgeExist, validateInterfaceName } from './utils';
import { INTERFACE_TYPE_LABEL, NETWORK_STATES, onInterfaceChangeType } from './utils/constants';
import { doesOVSBridgeExist, validateInterfaceName } from './utils/utils';

type PolicyInterfaceProps = {
id: number;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/components/PolicyForm/PolicyInterfaceExpandable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import { MinusCircleIcon } from '@patternfly/react-icons';
import { useNMStateTranslation } from '@utils/hooks/useNMStateTranslation';

import DeleteInterfaceModal from './components/DeleteInterfaceModal';
import { onInterfaceChangeType } from './constants';
import { onInterfaceChangeType } from './utils/constants';
import { doesOVSBridgeExist, getExpandableTitle } from './utils/utils';
import PolicyInterface from './PolicyInterface';
import { doesOVSBridgeExist, getExpandableTitle } from './utils';

type PolicyInterfacesExpandableProps = {
policy: V1NodeNetworkConfigurationPolicy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useNMStateTranslation } from '@utils/hooks/useNMStateTranslation';
import { getPolicyInterfaces, getPolicyInterfacesByType } from '@utils/resources/policies/utils';

import PolicyFormOVSBridgeMapping from '../components/PolicyFormOVSBridgeMapping';
import { doesOVSBridgeExist } from '../utils';
import { doesOVSBridgeExist } from '../utils/utils';

import InterfaceDetailsExpandableSection from './components/InterfaceDetailsExpandableSection';

Expand Down
104 changes: 43 additions & 61 deletions src/utils/components/PolicyForm/PolicyWizard/PolicyWizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@ import React, { FC, MouseEventHandler, useCallback, useState } from 'react';
import { useNMStateTranslation } from 'src/utils/hooks/useNMStateTranslation';
import { Updater } from 'use-immer';

import { InterfaceType, V1NodeNetworkConfigurationPolicy } from '@kubevirt-ui/kubevirt-api/nmstate';
import { V1NodeNetworkConfigurationPolicy } from '@kubevirt-ui/kubevirt-api/nmstate';
import { Wizard, WizardStep } from '@patternfly/react-core';
import NodesConfigurationStep from '@utils/components/PolicyForm/PolicyWizard/steps/NodesConfigurationStep/NodesConfigurationStep';
import SettingsStep from '@utils/components/PolicyForm/PolicyWizard/steps/SettingsStep/SettingsStep';
import { getOVNLocalnet } from '@utils/components/PolicyForm/PolicyWizard/utils/selectors';
import { ConnectionOption } from '@utils/components/PolicyForm/PolicyWizard/utils/types';
import {
getUplinkConnectionOption,
uplinkSettingsValid,
} from '@utils/components/PolicyForm/PolicyWizard/utils/utils';
import { getName } from '@utils/components/resources/selectors';
import { isEmpty } from '@utils/helpers';

import { ensureNoEmptyBridgeMapping } from '../utils';
import { ensureNoEmptyBridgeMapping } from '../utils/utils';

import BasicInfoStep from './BasicInfoStep';
import InterfacesStep from './InterfacesStep';
import ReviewStep from './ReviewStep';
import NetworkIdentity from './steps/NetworkIdentity/NetworkIdentity';
import ReviewStep from './steps/ReviewStep/ReviewStep';
import UplinkConnectionStep from './steps/UplinkConnectionStep/UplinkConnectionStep';

import '../policy-form.scss';
import './policy-wizard.scss';
import './PolicyWizard.scss';

type PolicyWizardProps = {
policy: V1NodeNetworkConfigurationPolicy;
Expand Down Expand Up @@ -53,76 +62,49 @@ const PolicyWizard: FC<PolicyWizardProps> = ({
<Wizard
onSave={onFormSubmit}
onClose={onClose}
onStepChange={(event, currentStep) => {
onStepChange={(_, currentStep) => {
onStepChange?.(currentStep.id);
}}
className="nmstate-policy-wizard policy-form-content"
>
<WizardStep
footer={{ isNextDisabled: isEmpty(policy.metadata.name) }}
id="policy-wizard-basicinfo"
name={t('Basic policy info')}
footer={{ isNextDisabled: isEmpty(getOVNLocalnet(policy)) }}
id="policy-wizard-network-identity"
name={t('Network identity')}
>
<BasicInfoStep policy={policy} setPolicy={setPolicy} />
<NetworkIdentity policy={policy} setPolicy={setPolicy} />
</WizardStep>
<WizardStep
id="policy-wizard-interfaces"
name={t('Policy interfaces')}
footer={{
nextButtonProps: { isLoading: loading },
isNextDisabled: loading,
nextButtonText: t('Save'),
}}
steps={[
<WizardStep
name={t('Basic interfaces')}
id="policy-wizard-basic-interfaces"
key="policy-wizard-basic-interfaces"
>
<InterfacesStep
policy={policy}
setPolicy={setPolicy}
interfaceTypes={[InterfaceType.ETHERNET]}
label={t('Ethernet')}
/>
</WizardStep>,
<WizardStep
name={t('Bonding')}
id="policy-wizard-bonding-interfaces"
key="policy-wizard-bonding-interfaces"
>
<InterfacesStep
policy={policy}
setPolicy={setPolicy}
interfaceTypes={[InterfaceType.BOND]}
label={t('Bonding')}
/>
</WizardStep>,
<WizardStep
name={t('Bridging')}
id="policy-wizard-bridge-interfaces"
key="policy-wizard-bridge-interfaces"
>
<InterfacesStep
policy={policy}
setPolicy={setPolicy}
interfaceTypes={[InterfaceType.LINUX_BRIDGE, InterfaceType.OVS_BRIDGE]}
label={t('Bridging')}
/>
</WizardStep>,
]}
></WizardStep>

footer={{ isNextDisabled: isEmpty(getName(policy)) }}
id="policy-wizard-basic-info"
name={t('Nodes configuration')}
>
<NodesConfigurationStep policy={policy} setPolicy={setPolicy} />
</WizardStep>
<WizardStep
id="policy-wizard-uplink-connection"
name={t('Uplink connection')}
footer={{ isNextDisabled: !uplinkSettingsValid(policy) }}
>
<UplinkConnectionStep policy={policy} setPolicy={setPolicy} />
</WizardStep>
<WizardStep
name={t('Settings')}
id="policy-wizard-configuration"
isDisabled={getUplinkConnectionOption(policy) === ConnectionOption.BREX}
>
<SettingsStep policy={policy} setPolicy={setPolicy} />
</WizardStep>
<WizardStep
footer={{
isNextDisabled: isEmpty(policy.metadata.name),
isNextDisabled: isEmpty(getName(policy)),
nextButtonProps: { isLoading: loading },
nextButtonText: t('Create policy'),
nextButtonText: t('Create network'),
}}
id="policy-wizard-review"
name={t('Review and create')}
>
<ReviewStep policy={policy} error={error} />
<ReviewStep policy={policy} creationError={error} setPolicy={setPolicy} />
</WizardStep>
</Wizard>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { useNMStateTranslation } from '@utils/hooks/useNMStateTranslation';
import { OVN_BRIDGE_MAPPINGS } from '@utils/resources/ovn/constants';
import { getOVNConfiguration } from '@utils/resources/policies/getters';

import { INTERFACE_TYPE_LABEL, onInterfaceChangeType } from '../../constants';
import { doesOVSBridgeExist } from '../../utils';
import { INTERFACE_TYPE_LABEL, onInterfaceChangeType } from '../../utils/constants';
import { doesOVSBridgeExist } from '../../utils/utils';

type BridgeTypeProps = {
policyInterface?: NodeNetworkConfigurationInterface;
Expand Down
Loading