Skip to content

Commit

Permalink
Adds support for HCI Phase 2<RHSTOR-4885>
Browse files Browse the repository at this point in the history
Adds list page for Clients
Adds Modal for generating onboarding token for clients
Updates ODF dashboard to add Client Status
Adds flags for Provider Mode
Disables StorageClass and BlockPool creation in Provider mode

Signed-off-by: Bipul Adhikari <[email protected]>
  • Loading branch information
bipuladh committed Dec 8, 2023
1 parent 046ef75 commit b8db10b
Show file tree
Hide file tree
Showing 19 changed files with 736 additions and 46 deletions.
18 changes: 16 additions & 2 deletions locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,9 @@
"Storage Systems": "Storage Systems",
"External object provider used capacity": "External object provider used capacity",
"Performance Card": "Performance Card",
"Storage Clients": "Storage Clients",
"0 connected": "0 connected",
"{{connected}} / {{total}} connected": "{{connected}} / {{total}} connected",
"System raw capacity": "System raw capacity",
"No data available": "No data available",
"Provider details": "Provider details",
Expand All @@ -942,6 +945,17 @@
"NamespaceStore details": "NamespaceStore details",
"Target Blob Container": "Target Blob Container",
"Num Volumes": "Num Volumes",
"Cluster ID": "Cluster ID",
"Openshift version": "Openshift version",
"Data Foundation version": "Data Foundation version",
"Last heartbeat": "Last heartbeat",
"ago": "ago",
"Client version is out of date": "Client version is out of date",
"Due to the mismatch in the client and provider version this provider cluster cannot be upgraded.": "Due to the mismatch in the client and provider version this provider cluster cannot be upgraded.",
"Storage clients": "Storage clients",
"Generate client onboarding token": "Generate client onboarding token",
"Data Foundation version sync": "Data Foundation version sync",
"Client onboarding token": "Client onboarding token",
"Raw Capacity": "Raw Capacity",
"Add Capacity": "Add Capacity",
"External": "External",
Expand Down Expand Up @@ -1159,6 +1173,7 @@
"Receive bandwidth": "Receive bandwidth",
"Node details": "Node details",
"Instance type": "Instance type",
"Rack": "Rack",
"External ID": "External ID",
"Node addresses": "Node addresses",
"Machine": "Machine",
Expand Down Expand Up @@ -1205,6 +1220,5 @@
"Cannot change resource name (original: \"{{name}}\", updated: \"{{newName}}\").": "Cannot change resource name (original: \"{{name}}\", updated: \"{{newName}}\").",
"Cannot change resource namespace (original: \"{{namespace}}\", updated: \"{{newNamespace}}\").": "Cannot change resource namespace (original: \"{{namespace}}\", updated: \"{{newNamespace}}\").",
"Cannot change resource kind (original: \"{{original}}\", updated: \"{{updated}}\").": "Cannot change resource kind (original: \"{{original}}\", updated: \"{{updated}}\").",
"Cannot change API group (original: \"{{apiGroup}}\", updated: \"{{newAPIGroup}}\").": "Cannot change API group (original: \"{{apiGroup}}\", updated: \"{{newAPIGroup}}\").",
"Rack": "Rack"
"Cannot change API group (original: \"{{apiGroup}}\", updated: \"{{newAPIGroup}}\").": "Cannot change API group (original: \"{{apiGroup}}\", updated: \"{{newAPIGroup}}\")."
}
16 changes: 13 additions & 3 deletions packages/ocs/dashboards/odf-system-dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import Tabs, { TabPage } from '@odf/shared/utils/Tabs';
import { useFlag } from '@openshift-console/dynamic-plugin-sdk';
import { RouteComponentProps } from 'react-router';
import { match as Match } from 'react-router-dom';
import { CEPH_FLAG, OCS_INDEPENDENT_FLAG } from '../../odf/features';
import {
CEPH_FLAG,
OCS_INDEPENDENT_FLAG,
PROVIDER_MODE,
} from '../../odf/features';
import { BlockPoolListPage } from '../block-pool/BlockPoolListPage';
import { CephBlockPoolModel } from '../models';
import OCSSystemDashboard from './ocs-system-dashboard';
Expand Down Expand Up @@ -44,10 +48,16 @@ const ODFSystemDashboard: React.FC<ODFSystemDashboardPageProps> = ({
]);
const isCephAvailable = useFlag(CEPH_FLAG);
const isExternal = useFlag(OCS_INDEPENDENT_FLAG);
const isProviderMode = useFlag(PROVIDER_MODE);

React.useEffect(() => {
const isBlockPoolAdded = pages.find((page) => page.href === blockPoolHref);
if (isCephAvailable && !isBlockPoolAdded && !isExternal) {
if (
isCephAvailable &&
!isBlockPoolAdded &&
!isExternal &&
!isProviderMode
) {
setPages((p) => [
...p,
{
Expand All @@ -60,7 +70,7 @@ const ODFSystemDashboard: React.FC<ODFSystemDashboardPageProps> = ({
if (isBlockPoolAdded && isExternal) {
setPages((p) => p.filter((page) => page.href !== blockPoolHref));
}
}, [isExternal, isCephAvailable, pages, setPages, t]);
}, [isExternal, isCephAvailable, isProviderMode, pages, setPages, t]);

const title = match.params.systemName;
const arePagesLoaded = pages.length > 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.odf-storageSystemPopup__item--margin {
.odf-status-card__popup--margin {
// Overriding PF default margin for FlexItem
margin-bottom: 0 !important;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from 'react';
import Status, { StatusPopupSection } from '@odf/shared/popup/status-popup';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import { HealthState } from '@openshift-console/dynamic-plugin-sdk';
import { Link } from 'react-router-dom';
import { Flex, FlexItem } from '@patternfly/react-core';
Expand All @@ -9,17 +8,19 @@ import {
ExclamationCircleIcon,
ExclamationTriangleIcon,
} from '@patternfly/react-icons';
import './storage-system-popup.scss';
import './status-card-popover.scss';

type SystemHealthMap = {
systemName: string;
export type ResourceHealthMap = {
itemName: string;
healthState: HealthState;
link: string;
link?: string;
extraTexts?: string[];
};

type StorageSystemPopopProps = {
systemHealthMap: SystemHealthMap[];
type StatusCardPopoverProps = {
resourceHealthMap: ResourceHealthMap[];
firstColumnName: string;
secondColumnName: string;
};

const healthStateToIcon = {
Expand All @@ -34,27 +35,32 @@ const healthStateToIcon = {
),
};

const StorageSystemPopup: React.FC<StorageSystemPopopProps> = ({
systemHealthMap,
const StatusCardPopover: React.FC<StatusCardPopoverProps> = ({
resourceHealthMap,
firstColumnName,
secondColumnName,
}) => {
const { t } = useCustomTranslation();
return (
<StatusPopupSection
firstColumn={t('Storage System')}
secondColumn={t('Health')}
firstColumn={firstColumnName}
secondColumn={secondColumnName}
>
{systemHealthMap.map((system) => (
{resourceHealthMap.map((resource) => (
<Status
key={system.systemName}
icon={healthStateToIcon[system.healthState]}
key={resource.itemName}
icon={healthStateToIcon[resource.healthState]}
>
<Flex direction={{ default: 'column' }}>
<FlexItem className="odf-storageSystemPopup__item--margin">
<Link to={system.link}>{system.systemName}</Link>
<FlexItem className="odf-status-card__popup--margin">
{resource.link ? (
<Link to={resource.link}>{resource.itemName}</Link>
) : (
<>{resource.itemName}</>
)}
</FlexItem>
{!!system.extraTexts && (
{!!resource.extraTexts && (
<FlexItem>
{system.extraTexts.map((extraText, i) => (
{resource.extraTexts.map((extraText, i) => (
<div className="text-muted" key={i}>
{extraText}
</div>
Expand All @@ -68,4 +74,4 @@ const StorageSystemPopup: React.FC<StorageSystemPopopProps> = ({
);
};

export default StorageSystemPopup;
export default StatusCardPopover;
52 changes: 48 additions & 4 deletions packages/odf/components/odf-dashboard/status-card/status-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import { useSafeK8sWatchResource } from '@odf/core/hooks';
import { K8sResourceObj } from '@odf/core/types';
import { useGetOCSHealth } from '@odf/ocs/hooks';
import { StorageConsumerKind } from '@odf/shared';
import { ODF_OPERATOR } from '@odf/shared/constants';
import HealthItem from '@odf/shared/dashboards/status-card/HealthItem';
import { healthStateMap } from '@odf/shared/dashboards/status-card/states';
Expand All @@ -21,8 +22,13 @@ import {
referenceForGroupVersionKind,
getOperatorHealthState,
} from '@odf/shared/utils';
import { HealthState } from '@openshift-console/dynamic-plugin-sdk';
import {
HealthState,
useFlag,
useK8sWatchResource,
} from '@openshift-console/dynamic-plugin-sdk';
import { HealthBody } from '@openshift-console/dynamic-plugin-sdk-internal';
import { useHistory } from 'react-router';
import {
Gallery,
GalleryItem,
Expand All @@ -32,9 +38,12 @@ import {
CardHeader,
CardTitle,
} from '@patternfly/react-core';
import { PROVIDER_MODE } from '../../../features';
import { StorageConsumerModel } from '../../../models';
import { getVendorDashboardLinkFromMetrics } from '../../utils';
import { StorageDashboard, STATUS_QUERIES } from '../queries';
import StorageSystemPopup from './storage-system-popup';
import StatusCardPopover from './status-card-popover';
import { getAggregateClientHealthState, getClientText } from './utils';
import './status-card.scss';

const operatorResource: K8sResourceObj = (ns) => ({
Expand Down Expand Up @@ -129,6 +138,23 @@ export const StatusCard: React.FC = () => {
csvLoadError
);

const isProviderMode = useFlag(PROVIDER_MODE);

const [clients, clientsLoaded, clientsLoadError] = useK8sWatchResource<
StorageConsumerKind[]
>({
kind: referenceForModel(StorageConsumerModel),
isList: true,
});

const clientAggregateHealth = getAggregateClientHealthState(clients);

const history = useHistory();

const redirectToListPage = React.useCallback(() => {
history.push('/odf/storage-clients');
}, [history]);

return (
<Card className="odfDashboard-card--height">
<CardHeader>
Expand All @@ -149,7 +175,11 @@ export const StatusCard: React.FC = () => {
title={pluralize(healthySystems.length, 'Storage System')}
state={HealthState.OK}
>
<StorageSystemPopup systemHealthMap={healthySystems} />
<StatusCardPopover
resourceHealthMap={healthySystems}
firstColumnName={t('Storage System')}
secondColumnName={t('Health')}
/>
</HealthItem>
</GalleryItem>
)}
Expand All @@ -160,10 +190,24 @@ export const StatusCard: React.FC = () => {
state={HealthState.ERROR}
maxWidth="35rem"
>
<StorageSystemPopup systemHealthMap={unHealthySystems} />
<StatusCardPopover
resourceHealthMap={unHealthySystems}
firstColumnName={t('Storage System')}
secondColumnName={t('Health')}
/>
</HealthItem>
</GalleryItem>
)}
{isProviderMode && clientsLoaded && !clientsLoadError && (
<GalleryItem>
<HealthItem
title={t('Storage Clients')}
state={clientAggregateHealth}
onClick={redirectToListPage}
details={getClientText(clients, t)}
/>
</GalleryItem>
)}
</Gallery>
</HealthBody>
</CardBody>
Expand Down
48 changes: 48 additions & 0 deletions packages/odf/components/odf-dashboard/status-card/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { StorageConsumerKind, StorageConsumerState } from '@odf/shared';
import { getTimeDifferenceInSeconds } from '@odf/shared/details-page/datetime';
import { HealthState } from '@openshift-console/dynamic-plugin-sdk';
import { TFunction } from 'i18next';

const getHealthAndTotalClientCounts = (clients: StorageConsumerKind[]) => {
const connectedClients = clients.filter(
(client) => client.status.state === StorageConsumerState.Ready
);

const healthyClients = connectedClients.filter(
(client) => getTimeDifferenceInSeconds(client.status.lastHeartbeat) < 120
);
const healthyClientsCount = healthyClients.length;
const totalClientsCount = clients.length;

return [healthyClientsCount, totalClientsCount];
};

export const getAggregateClientHealthState = (
clients: StorageConsumerKind[] = []
) => {
const [healthyClientsCount, totalClientsCount] =
getHealthAndTotalClientCounts(clients);
if (totalClientsCount === healthyClientsCount && totalClientsCount > 0) {
return HealthState.OK;
}
if (totalClientsCount > healthyClientsCount) {
return HealthState.ERROR;
}
if (totalClientsCount === 0) {
return HealthState.NOT_AVAILABLE;
}
return HealthState.UNKNOWN;
};

export const getClientText = (clients: StorageConsumerKind[], t: TFunction) => {
const [healthyClientsCount, totalClientsCount] =
getHealthAndTotalClientCounts(clients);
if (totalClientsCount === 0) {
return t('0 connected');
} else {
return t('{{connected}} / {{total}} connected', {
connected: healthyClientsCount,
total: totalClientsCount,
});
}
};
Loading

0 comments on commit b8db10b

Please sign in to comment.