Skip to content

Commit

Permalink
[WIP] NNS topology
Browse files Browse the repository at this point in the history
Signed-off-by: Aviv Turgeman <[email protected]>
  • Loading branch information
avivtur committed Aug 21, 2024
1 parent 266315a commit 38ca5fe
Show file tree
Hide file tree
Showing 34 changed files with 1,852 additions and 50 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/on_pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ jobs:
uses: cypress-io/[email protected]
with:
start: yarn serve-build, yarn start-console
wait-on: "http://localhost:9001"
wait-on: 'http://localhost:9001'
wait-on-timeout: 120
browser: chrome
headed: false

Expand Down
2 changes: 1 addition & 1 deletion cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ export default defineConfig({
},
viewportWidth: 1920,
viewportHeight: 1080,
defaultCommandTimeout: 40_000,
defaultCommandTimeout: 60_000,
});
4 changes: 2 additions & 2 deletions cypress/e2e/NewPolicy.spec.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('Create new policy with form', () => {
it('with bridge interface', () => {
const testPolicyName = 'test-bridge-policy-name';
cy.visit('/k8s/cluster/nmstate.io~v1~NodeNetworkConfigurationPolicy');
cy.contains('button[type="button"]', 'Create').click();
cy.byTestID('item-create').click();

cy.contains('button', 'From Form').click();

Expand All @@ -42,7 +42,7 @@ describe('Create new policy with form', () => {
it('with bridge and bond interface', () => {
const testPolicyName = 'test-bridge-bond-policy-name';
cy.visit('/k8s/cluster/nmstate.io~v1~NodeNetworkConfigurationPolicy');
cy.contains('button[type="button"]', 'Create').click();
cy.byTestID('item-create').click();

cy.contains('button', 'From Form').click();

Expand Down
10 changes: 10 additions & 0 deletions locales/en/plugin__nmstate-console-plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"{{annotationsCount}} Annotations": "{{annotationsCount}} Annotations",
"{{matchingNodeText}} matching": "{{matchingNodeText}} matching",
"{{modelName}} details": "{{modelName}} details",
"{{qualifiedNodesCount}} matching Nodes found": "{{qualifiedNodesCount}} matching Nodes found",
"<0>List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.</0><1><0>{{kind}}</0><1>metadata</1><2>ownerReferences</2></1>": "<0>List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.</0><1><0>{{kind}}</0><1>metadata</1><2>ownerReferences</2></1>",
"Aborted": "Aborted",
"Actions": "Actions",
"Add another interface to the policy": "Add another interface to the policy",
Expand All @@ -10,6 +12,7 @@
"Add Option": "Add Option",
"Aggregation mode": "Aggregation mode",
"An error occurred": "An error occurred",
"Annotations": "Annotations",
"Apply this NodeNetworkConfigurationPolicy only to specific subsets of nodes using the node selector": "Apply this NodeNetworkConfigurationPolicy only to specific subsets of nodes using the node selector",
"Are you sure you want to remove {{interfaceType}} interface <5>{{name}}</5>?": "Are you sure you want to remove {{interfaceType}} interface <5>{{name}}</5>?",
"Auto-DNS": "Auto-DNS",
Expand All @@ -30,6 +33,7 @@
"Create": "Create",
"Create NodeNetworkConfigurationPolicy": "Create NodeNetworkConfigurationPolicy",
"Create NodeNetworkConfigurationPolicy error": "Create NodeNetworkConfigurationPolicy error",
"Created at": "Created at",
"Created interfaces cannot be removed": "Created interfaces cannot be removed",
"Delete": "Delete",
"Delete NodeNetworkConfigurationPolicy?": "Delete NodeNetworkConfigurationPolicy?",
Expand Down Expand Up @@ -58,6 +62,7 @@
"Explore {{kind}} list": "Explore {{kind}} list",
"Failing": "Failing",
"Features": "Features",
"Filter": "Filter",
"From Form": "From Form",
"Id": "Id",
"Interface name": "Interface name",
Expand All @@ -73,6 +78,7 @@
"IPV4 address": "IPV4 address",
"IPv6": "IPv6",
"Key": "Key",
"Labels": "Labels",
"Learn more": "Learn more",
"List of network interfaces that should be created, modified, or removed, as a part of this policy.": "List of network interfaces that should be created, modified, or removed, as a part of this policy.",
"LLDP": "LLDP",
Expand All @@ -82,6 +88,7 @@
"MAC Address": "MAC Address",
"Matched nodes": "Matched nodes",
"Matched nodes summary": "Matched nodes summary",
"More info: ": "More info: ",
"MTU": "MTU",
"Name": "Name",
"Neighbors": "Neighbors",
Expand All @@ -91,15 +98,18 @@
"No matching Nodes found for the labels": "No matching Nodes found for the labels",
"No node network configuration policies defined yet": "No node network configuration policies defined yet",
"No NodeNetworkStates found": "No NodeNetworkStates found",
"No owner": "No owner",
"Node network is configured and managed by NM state. Create a node network configuration policy to describe the requested network configuration on your nodes in the cluster. The node network configuration enactment reports the netwrok policies enacted upon each node.": "Node network is configured and managed by NM state. Create a node netowrk configuration policy to describe the requested network configuration on your nodes in the cluster. The node network configuration enactment reports the netwrok policies enacted upon each node.",
"Node Selector": "Node Selector",
"NodeNetworkConfigurationPolicy": "NodeNetworkConfigurationPolicy",
"NodeNetworkState": "NodeNetworkState",
"nodes": "nodes",
"None": "None",
"Not available": "Not available",
"Open vSwitch bridge mapping": "Open vSwitch bridge mapping",
"OVN localnet name": "OVN localnet name",
"OVS bridge name": "OVS bridge name",
"Owner": "Owner",
"Pending": "Pending",
"Please <2>try again</2>.": "Please <2>try again</2>.",
"Policy details": "Policy details",
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"destroy-cluster": "./scripts/clean-cluster.sh",
"login-cluster": "oc login https://127.0.0.1:6443 --token abcdef.0123456789abcdef",
"cypress": "cypress run --browser chrome",
"cypress:open": "cypress open",
"i18n": "i18next \"src/**/*.{js,jsx,ts,tsx}\" [-oc] -c i18next-parser.config.mjs",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
Expand All @@ -34,6 +35,7 @@
"@patternfly/react-icons": "^5.1.1",
"@patternfly/react-core": "5.1.1",
"@patternfly/react-table": "^5.1.1",
"@patternfly/react-topology": "5.2.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.0.0",
"@testing-library/react-hooks": "^8.0.1",
Expand Down
4 changes: 1 addition & 3 deletions src/console-models/NodeNetworkStateModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { K8sModel } from '@openshift-console/dynamic-plugin-sdk/lib/api/common-t

import { modelToGroupVersionKind, modelToRef } from './modelUtils';

const NodeNetworkStateModel: K8sModel = {
export const NodeNetworkStateModel: K8sModel = {
label: 'NodeNetworkState',
labelPlural: 'NodeNetworkStates',
apiVersion: 'v1beta1',
Expand All @@ -17,5 +17,3 @@ const NodeNetworkStateModel: K8sModel = {

export const NodeNetworkStateModelGroupVersionKind = modelToGroupVersionKind(NodeNetworkStateModel);
export const NodeNetworkStateModelRef = modelToRef(NodeNetworkStateModel);

export default NodeNetworkStateModel;
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ export interface NodeNetworkConfigurationInterface {
'base-iface': string;
protocol?: string;
};

patch?: {
peer?: string;
};
}
17 changes: 17 additions & 0 deletions src/utils/components/DetailItem/DetailItem.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.description-item__title {
justify-content: space-between;
flex: 1;
}

.margin-top {
margin-top: var(--pf-v5-global--spacer--md);
}

.DescriptionItemHeader {
&--list-term {
.pf-c-description-list__text {
align-items: center;
display: inline-flex;
}
}
}
116 changes: 116 additions & 0 deletions src/utils/components/DetailItem/DetailItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, { FC, ReactNode } from 'react';

import {
Button,
DescriptionListDescription,
DescriptionListGroup,
DescriptionListTermHelpText,
Flex,
FlexItem,
} from '@patternfly/react-core';
import { PencilAltIcon } from '@patternfly/react-icons';
import { useNMStateTranslation } from '@utils/hooks/useNMStateTranslation';

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

import './DetailItem.scss';

type DetailItemProps = {
bodyContent?: ReactNode;
breadcrumb?: string;
className?: string;
'data-test-id'?: string;
descriptionData: ReactNode | string;
descriptionHeader?: ReactNode;
editOnTitleJustify?: boolean;
isDisabled?: boolean;
isEdit?: boolean;
isPopover?: boolean;
label?: ReactNode;
messageOnDisabled?: string;
moreInfoURL?: string;
onEditClick?: () => void;
showEditOnTitle?: boolean;
};

const DetailItem: FC<DetailItemProps> = ({
bodyContent,
breadcrumb,
className,
'data-test-id': testId,
descriptionData,
descriptionHeader,
editOnTitleJustify = false,
isDisabled,
isEdit,
isPopover,
label,
messageOnDisabled,
moreInfoURL,
onEditClick,
showEditOnTitle,
}) => {
const { t } = useNMStateTranslation();
const NotAvailable = <span className="text-muted">{t('Not available')}</span>;

const description = (
<EditButtonWithTooltip
isEditable={!isDisabled}
onEditClick={onEditClick}
testId={testId}
tooltipContent={messageOnDisabled}
>
{descriptionData ?? NotAvailable}
</EditButtonWithTooltip>
);

return (
<DescriptionListGroup className={`pf-c-description-list__group ${className && className}`}>
<DescriptionListTermHelpText className="pf-c-description-list__term">
<Flex
justifyContent={{
default: editOnTitleJustify ? 'justifyContentSpaceBetween' : 'justifyContentFlexStart',
}}
>
{(bodyContent || breadcrumb || descriptionHeader || label || moreInfoURL) && (
<FlexItem>
<DetailItemHeader
bodyContent={bodyContent}
breadcrumb={breadcrumb}
descriptionHeader={descriptionHeader}
isPopover={isPopover}
label={label}
moreInfoURL={moreInfoURL}
/>
</FlexItem>
)}
{isEdit && showEditOnTitle && (
<FlexItem>
<Button
data-test-id={`${testId}-edit`}
isDisabled={isDisabled}
isInline
onClick={onEditClick}
type="button"
variant="link"
>
{t('Edit')}
<PencilAltIcon className="co-icon-space-l pf-v5-c-button-icon--plain" />
</Button>
</FlexItem>
)}
</Flex>
</DescriptionListTermHelpText>

<DescriptionListDescription
className="pf-c-description-list__description"
data-test-id={testId}
>
{isEdit && !showEditOnTitle ? description : descriptionData}
</DescriptionListDescription>
</DescriptionListGroup>
);
};

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

import {
Breadcrumb,
BreadcrumbItem,
DescriptionListTerm,
DescriptionListTermHelpTextButton,
Popover,
} from '@patternfly/react-core';
import { useNMStateTranslation } from '@utils/hooks/useNMStateTranslation';

import './DetailItem.scss';

type DetailItemHeaderProps = {
bodyContent: ReactNode;
breadcrumb?: string;
descriptionHeader: ReactNode;
isPopover: boolean;
label?: ReactNode;
maxWidth?: string;
moreInfoURL?: string;
};

export const DetailItemHeader: FC<DetailItemHeaderProps> = ({
bodyContent,
breadcrumb,
descriptionHeader,
isPopover,
label,
maxWidth,
moreInfoURL,
}) => {
const { t } = useNMStateTranslation();

if (isPopover && bodyContent) {
return (
<Popover
bodyContent={
<>
{bodyContent}
{moreInfoURL && (
<>
{t('More info: ')}
<a href={moreInfoURL}>{moreInfoURL}</a>
</>
)}
{breadcrumb && (
<div className="margin-top">
<Breadcrumb>
{breadcrumb.split('.').map((item) => (
<BreadcrumbItem key={item}>{item}</BreadcrumbItem>
))}
</Breadcrumb>
</div>
)}
</>
}
hasAutoWidth
headerContent={descriptionHeader}
maxWidth={maxWidth || '30rem'}
>
<DescriptionListTermHelpTextButton className="pf-c-description-list__text">
{descriptionHeader}
</DescriptionListTermHelpTextButton>
</Popover>
);
}

return (
<DescriptionListTerm className="DescriptionItemHeader--list-term">
{descriptionHeader} {label}
</DescriptionListTerm>
);
};
Loading

0 comments on commit 38ca5fe

Please sign in to comment.