Skip to content

Commit

Permalink
Add list page for Storage Systems and Inject it via Horizontal Nav
Browse files Browse the repository at this point in the history
  • Loading branch information
bipuladh committed Jul 23, 2021
1 parent cdea2e7 commit 6810de1
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,8 @@
"The last saved values will be updated": "The last saved values will be updated",
"Enable Thick Provisioning": "Enable Thick Provisioning",
"By enabling thick-provisioning, volumes will allocate the requested capacity upon volume creation. Volume creation will be slower when thick-provisioning is enabled.": "By enabling thick-provisioning, volumes will allocate the requested capacity upon volume creation. Volume creation will be slower when thick-provisioning is enabled.",
"Used capacity": "Used capacity",
"Storage Systems": "Storage Systems",
"Storage status represents the health status of Openshift Container Storage's StorageCluster.": "Storage status represents the health status of Openshift Container Storage's StorageCluster.",
"Health": "Health",
"Openshift Container Storage": "Openshift Container Storage",
Expand Down Expand Up @@ -587,7 +589,6 @@
"Delete": "Delete",
"StorageClasses": "StorageClasses",
"Replicas": "Replicas",
"Used capacity": "Used capacity",
"Mirroring status": "Mirroring status",
"Overall image health": "Overall image health",
"Compression status": "Compression status",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { K8sKind } from '@console/dynamic-plugin-sdk';
import { K8sResourceKind, referenceForModel } from '@console/internal/module/k8s';
import { Kebab } from '@console/internal/components/utils';
import { addCapacityModal } from '../modals/add-capacity-modal/add-capacity-modal';
import { OCSServiceModel } from '../../models';

const addStorage = (kind: K8sKind, resource: K8sResourceKind) => ({
labelKey: 'ceph-storage-plguin~Add Capacity',
callback: () => addCapacityModal({ kind, ocsConfig: resource }),
});

export const getGenericActions = () => [...Kebab.factory.common];

export const getActionsForOCS = () => [addStorage, ...getGenericActions()];

export const getActions = (kind: string) => {
if (referenceForModel(OCSServiceModel).toLowerCase() === kind.toLowerCase())
return getActionsForOCS();
return getGenericActions();
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { Kebab, ResourceKebab } from '@console/internal/components/utils';
import {
RowFunction,
TableData,
TableRow,
Table,
ListPage,
} from '@console/internal/components/factory';
import { referenceForGroupVersionKind, referenceForModel } from '@console/internal/module/k8s';
import { StatusIcon } from '@console/shared';
import { sortable } from '@patternfly/react-table';
import { ClusterServiceVersionModel } from '@console/operator-lifecycle-manager';
import { usePrometheusPoll } from '@console/internal/components/graphs/prometheus-poll-hook';
import { PrometheusEndpoint } from '@console/internal/components/graphs/helpers';
import ODFSystemLink from './system-link';
import { getGVK, SystemMetrics, normalizeMetrics } from './utils';
import { getActions } from './actions';
import { StorageSystemModel } from '../../models';
import { StorageSystemKind } from '../../types';
import { ODF_QUERIES, ODFQueries } from '../../queries';

const tableColumnClasses = [
'pf-u-w-25-on-xl',
'pf-m-hidden pf-m-visible-on-md',
'pf-m-hidden pf-m-visible-on-md',
'pf-m-hidden pf-m-visible-on-lg pf-u-w-10-on-xl',
'pf-m-hidden pf-m-visible-on-lg pf-u-w-10-on-xl',
'pf-m-hidden pf-m-visible-on-lg pf-u-w-10-on-xl',
'pf-m-hidden pf-m-visible-on-lg pf-u-w-10-on-xl',
Kebab.columnClass,
];

const SystemTableRow: RowFunction<StorageSystemKind> = ({ obj, index, key, style, customData }) => {
const { apiGroup, apiVersion, kind } = getGVK(obj.spec.kind);
const systemKind = referenceForGroupVersionKind(apiGroup)(apiVersion)(kind);
const systemName = obj.spec.name;

const { rawCapacity, usedCapacity, iops, throughput, latency } =
(customData as SystemMetrics)?.metrics?.[systemName] || {};

return (
<TableRow id={obj.metadata.uid} index={index} trKey={key} style={style}>
<TableData className={tableColumnClasses[0]}>
<ODFSystemLink kind={systemKind} name={systemName} />
</TableData>
<TableData className={tableColumnClasses[1]}>
<span>
<StatusIcon status={obj?.status?.phase} /> {obj?.status?.phase}
</span>
</TableData>
<TableData className={tableColumnClasses[2]}>{rawCapacity?.string || '-'}</TableData>
<TableData className={tableColumnClasses[3]}>{usedCapacity?.string || '-'}</TableData>
<TableData className={tableColumnClasses[4]}>{iops?.string || '-'}</TableData>
<TableData className={tableColumnClasses[5]}>{throughput?.string || '-'}</TableData>
<TableData className={tableColumnClasses[6]}>{latency?.string || '-'}</TableData>
<TableData className={tableColumnClasses[7]}>
<ResourceKebab
actions={getActions(systemKind)}
resource={obj}
kind={referenceForModel(StorageSystemModel)}
/>
</TableData>
</TableRow>
);
};

const StorageSystemList: React.FC = (props) => {
const { t } = useTranslation();
const Header = () => {
return [
{
title: t('ceph-storage-plugin~Name'),
sortField: 'metadata.name',
transforms: [sortable],
props: { className: tableColumnClasses[0] },
},
{
title: t('ceph-storage-plugin~Status'),
props: { className: tableColumnClasses[1] },
},
{
title: t('ceph-storage-plugin~Raw Capacity'),
props: { className: tableColumnClasses[2] },
},
{
title: t('ceph-storage-plugin~Used capacity'),
props: { className: tableColumnClasses[3] },
},
{
title: t('ceph-storage-plugin~IOPS'),
props: { className: tableColumnClasses[4] },
},
{
title: t('ceph-storage-plugin~Throughput'),
props: { className: tableColumnClasses[5] },
},
{
title: t('ceph-storage-plugin~Latency'),
props: { className: tableColumnClasses[6] },
},
{
title: '',
props: { className: tableColumnClasses[7] },
},
];
};
Header.displayName = 'SSHeader';

const [latency] = usePrometheusPoll({
endpoint: PrometheusEndpoint.QUERY,
query: ODF_QUERIES[ODFQueries.LATENCY],
});
const [iops] = usePrometheusPoll({
endpoint: PrometheusEndpoint.QUERY,
query: ODF_QUERIES[ODFQueries.IOPS],
});
const [throughput] = usePrometheusPoll({
endpoint: PrometheusEndpoint.QUERY,
query: ODF_QUERIES[ODFQueries.THROUGHPUT],
});
const [rawCapacity] = usePrometheusPoll({
endpoint: PrometheusEndpoint.QUERY,
query: ODF_QUERIES[ODFQueries.RAW_CAPACITY],
});
const [usedCapacity] = usePrometheusPoll({
endpoint: PrometheusEndpoint.QUERY,
query: ODF_QUERIES[ODFQueries.USED_CAPACITY],
});

const normalizedMetrics = normalizeMetrics(latency, throughput, rawCapacity, usedCapacity, iops);

return (
<Table
{...props}
customData={normalizedMetrics}
aria-label={t('ceph-storage-plugin~Storage Systems')}
Header={Header}
Row={SystemTableRow}
virtualize
/>
);
};

const StorageSystemListPage: React.FC = (props) => {
const createProps = {
to: `/k8s/ns/openshift-storage/${referenceForModel(
ClusterServiceVersionModel,
)}/odf-operator/${referenceForModel(StorageSystemModel)}/~new`,
};
return (
<ListPage
{...props}
showTitle={false}
ListComponent={StorageSystemList}
kind={referenceForModel(StorageSystemModel)}
canCreate
createProps={createProps}
/>
);
};

export default StorageSystemListPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';
import { Link } from 'react-router-dom';
import { ResourceIcon } from '@console/internal/components/utils';

type ODFSystemLinkProps = {
kind: string;
name: string;
};

const ODFSystemLink: React.FC<ODFSystemLinkProps> = ({ kind, name }) => {
const path = `/odf/system/${kind}/${name}`;
return (
<span>
<ResourceIcon kind={kind} />
<Link to={path}>{name}</Link>
</span>
);
};

export default ODFSystemLink;
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { HumanizeResult } from '@console/internal/components/utils';
import { PrometheusResponse } from '@console/internal/module/k8s';

// Operator uses`<kind>.<apiGroup>/<apiVersion>` don't ask why
export const getGVK = (weirdLabel: string) => {
const kind = weirdLabel.slice(0, weirdLabel.indexOf('.'));
const apiGroup = weirdLabel.slice(weirdLabel.indexOf('.') + 1, weirdLabel.indexOf('/'));
const apiVersion = weirdLabel.slice(weirdLabel.indexOf('/') + 1, weirdLabel.length);
return { kind, apiGroup, apiVersion };
};

export type SystemMetrics = {
metrics: {
[systeName: string]: {
rawCapacity: HumanizeResult;
usedCapacity: HumanizeResult;
iops: HumanizeResult;
throughput: HumanizeResult;
latency: HumanizeResult;
};
};
};

type MetricNormalize = (
latency: PrometheusResponse,
throughput: PrometheusResponse,
rawCapacity: PrometheusResponse,
usedCapacity: PrometheusResponse,
iops: PrometheusResponse,
) => SystemMetrics['metrics'];

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const normalizeMetrics: MetricNormalize = (
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_latency,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_throughput,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_rawCapacity,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_usedCapacity,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_iops,
) => {
// Todo(bipuladh): Add parsing logic for above items
return {
metrics: {} as any,
};
};
18 changes: 18 additions & 0 deletions frontend/packages/ceph-storage-plugin/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,24 @@ const plugin: Plugin<ConsumedExtensions> = [
},
},
},
// Adding this Extension because dynamic endpoint is not avbl
// Todo(bipuladh): Remove once SDK is mature enough to support list page
{
type: 'HorizontalNavTab',
properties: {
model: models.StorageSystemModel,
page: {
name: '%ceph-storage-plugin~Storage Systems%',
href: 'systems',
},
loader: async () =>
(
await import(
'./components/odf-system/odf-system-list' /* webpackChunkName: "odf-system-list" */
)
).default,
},
},
];

export default plugin;
1 change: 1 addition & 0 deletions frontend/packages/ceph-storage-plugin/src/queries/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './ceph-queries';
export * from './odf-queries';
15 changes: 15 additions & 0 deletions frontend/packages/ceph-storage-plugin/src/queries/odf-queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export enum ODFQueries {
LATENCY = 'LAT',
IOPS = 'IOPS',
THROUGHPUT = 'THROUGHPUT',
RAW_CAPACITY = 'RAW_CAP',
USED_CAPACITY = 'USED_CAP',
}

export const ODF_QUERIES: { [key in ODFQueries]: string } = {
[ODFQueries.LATENCY]: 'odf_system_latency_seconds',
[ODFQueries.IOPS]: 'odf_system_iops_total_bytes',
[ODFQueries.THROUGHPUT]: 'odf_system_throughput_total_bytes',
[ODFQueries.RAW_CAPACITY]: 'odf_system_raw_capacity_total_bytes',
[ODFQueries.USED_CAPACITY]: 'odf_system_raw_capacity_used_bytes',
};
4 changes: 4 additions & 0 deletions frontend/packages/ceph-storage-plugin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,4 +317,8 @@ export type StorageSystemKind = K8sResourceCommon & {
// namespace describes the name of managed storage vendor CR
namespace: string;
};
status: {
phase?: string;
conditions?: any;
};
};

0 comments on commit 6810de1

Please sign in to comment.