Skip to content

Commit

Permalink
Merge pull request #910 from sgratch/add-vms-migration-chart
Browse files Browse the repository at this point in the history
🐾 Create a new Metrics tab - part 5 (last)
  • Loading branch information
yaacov authored Feb 13, 2024
2 parents 34ea621 + c2054f5 commit d3c6789
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,9 @@
"View details": "View details",
"View provider details": "View provider details",
"Virtual Machine Migrations": "Virtual Machine Migrations",
"Virtual Machine Migrations (last 24 hours)": "Virtual Machine Migrations (last 24 hours)",
"Virtual Machine Migrations (last 31 days)": "Virtual Machine Migrations (last 31 days)",
"Virtual Machine Migrations (last 7 days)": "Virtual Machine Migrations (last 7 days)",
"Virtual machines": "Virtual machines",
"Virtual Machines": "Virtual Machines",
"VMs": "VMs",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DateTime, Interval } from 'luxon';
import { useForkliftTranslation } from 'src/utils/i18n';

import { MigrationModelGroupVersionKind, V1beta1Migration } from '@kubev2v/types';
import { V1beta1MigrationStatusVms } from '@kubev2v/types/dist/models/V1beta1MigrationStatusVms';
import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';
import { Chart, ChartAxis, ChartBar, ChartGroup, ChartTooltip } from '@patternfly/react-charts';
import {
Expand Down Expand Up @@ -33,8 +34,10 @@ enum TimeRangeOptions {
Last24H = 'Last24H',
}

const toStarted = (m: V1beta1Migration): string => m.status.started;
const toFinished = (m: V1beta1Migration): string => m.status.completed;
const toStartedMigration = (m: V1beta1Migration): string => m.status.started;
const toStartedVmMigration = (v: V1beta1MigrationStatusVms): string => v.started;
const toFinishedMigration = (m: V1beta1Migration): string => m.status.completed;
const toFinishedVmMigration = (v: V1beta1MigrationStatusVms): string => v.completed;
const hasTimestamp = (timestamp: string) => timestamp && DateTime.fromISO(timestamp).isValid;
const toDateTime = (timestamp: string): DateTime => DateTime.fromISO(timestamp);
const isLast7Days = (date: DateTime) => date.diffNow('days').get('days') <= 7;
Expand All @@ -46,23 +49,29 @@ const toHourLabel = (date: DateTime): string => date.toFormat('HH:mm');
const TimeRangeOptionsDictionary = {
Last7Days: {
// t('Migrations (last 7 days)')
labelKey: 'Migrations (last 7 days)',
migrationsLabelKey: 'Migrations (last 7 days)',
// t('Virtual Machine Migrations (last 7 days)')
vmMigrationsLabelKey: 'Virtual Machine Migrations (last 7 days)',
span: { days: 7 },
bucket: { day: 1 },
unit: 'day',
filter: isLast7Days,
},
Last31Days: {
// t('Migrations (last 31 days)')
labelKey: 'Migrations (last 31 days)',
migrationsLabelKey: 'Migrations (last 31 days)',
// t('Virtual Machine Migrations (last 31 days)')
vmMigrationsLabelKey: 'Virtual Machine Migrations (last 31 days)',
span: { days: 31 },
bucket: { day: 4 },
unit: 'day',
filter: isLast31Days,
},
Last24H: {
// t('Migrations (last 24 hours)')
labelKey: 'Migrations (last 24 hours)',
migrationsLabelKey: 'Migrations (last 24 hours)',
// t('Virtual Machine Migrations (last 24 hours)')
vmMigrationsLabelKey: 'Virtual Machine Migrations (last 24 hours)',
span: { hours: 24 },
bucket: { hour: 4 },
unit: 'hour',
Expand Down Expand Up @@ -90,12 +99,10 @@ const groupByBucket = (acc: [Interval, DateTime[]][], date: DateTime) =>
);

const toDataPoints = (
allMigrations: V1beta1Migration[],
toTimestamp: (m: V1beta1Migration) => string,
filteredMigrations: string[],
selectedTimeRange: TimeRangeOptions,
): MigrationDataPoint[] =>
allMigrations
.map(toTimestamp)
filteredMigrations
.filter(hasTimestamp)
.map(toDateTime)
.filter(TimeRangeOptionsDictionary[selectedTimeRange].filter)
Expand All @@ -110,13 +117,29 @@ const toDataPoints = (
value: points.length,
}));

const toDataPointsForMigrations = (
allMigrations: V1beta1Migration[],
toTimestamp: (m: V1beta1Migration) => string,
selectedTimeRange: TimeRangeOptions,
): MigrationDataPoint[] => toDataPoints(allMigrations.map(toTimestamp), selectedTimeRange);

const toDataPointsForVmMigrations = (
allVmMigrations: V1beta1MigrationStatusVms[],
toTimestamp: (m: V1beta1MigrationStatusVms) => string,
selectedTimeRange: TimeRangeOptions,
): MigrationDataPoint[] => toDataPoints(allVmMigrations.map(toTimestamp), selectedTimeRange);

export const ChartsCard: React.FC<MigrationsCardProps> = () => {
const { t } = useForkliftTranslation();
const [isDropdownOpened, setIsDropdownOpened] = useState(false);
const onToggle = () => setIsDropdownOpened(!isDropdownOpened);
const [selectedTimeRange, setSelectedTimeRange] = useState<TimeRangeOptions>(
const [isMigrationsDropdownOpened, setIsMigrationsDropdownOpened] = useState(false);
const [isVmMigrationsDropdownOpened, setIsVmMigrationsDropdownOpened] = useState(false);
const onToggleMigrations = () => setIsMigrationsDropdownOpened(!isMigrationsDropdownOpened);
const onToggleVmMigrations = () => setIsVmMigrationsDropdownOpened(!isVmMigrationsDropdownOpened);
const [selectedTimeRangeMigrations, setSelectedTimeRangeMigrations] = useState<TimeRangeOptions>(
TimeRangeOptions.Last7Days,
);
const [selectedTimeRangeVmMigrations, setSelectedTimeRangeVmMigrations] =
useState<TimeRangeOptions>(TimeRangeOptions.Last7Days);
const [migrations] = useK8sWatchResource<V1beta1Migration[]>({
groupVersionKind: MigrationModelGroupVersionKind,
namespaced: true,
Expand All @@ -127,20 +150,60 @@ export const ChartsCard: React.FC<MigrationsCardProps> = () => {
failed: MigrationDataPoint[];
succeeded: MigrationDataPoint[];
} = {
running: toDataPoints(
running: toDataPointsForMigrations(
migrations.filter((m) => m?.status?.conditions?.find((it) => it?.type === 'Running')),
toStarted,
selectedTimeRange,
toStartedMigration,
selectedTimeRangeMigrations,
),
failed: toDataPoints(
failed: toDataPointsForMigrations(
migrations.filter((m) => m?.status?.conditions?.find((it) => it?.type === 'Failed')),
toFinished,
selectedTimeRange,
toFinishedMigration,
selectedTimeRangeMigrations,
),
succeeded: toDataPoints(
succeeded: toDataPointsForMigrations(
migrations.filter((m) => m?.status?.conditions?.find((it) => it?.type === 'Succeeded')),
toFinished,
selectedTimeRange,
toFinishedMigration,
selectedTimeRangeMigrations,
),
};

const VmMigrationsDataPoints: {
running: MigrationDataPoint[];
failed: MigrationDataPoint[];
succeeded: MigrationDataPoint[];
} = {
running: toDataPointsForVmMigrations(
migrations
.map((migration) =>
migration?.status?.vms.filter(
(vm) => vm?.phase !== 'Completed' && vm?.phase != 'Canceled',
),
)
.reduce((append, vm) => append.concat(vm), []),
toStartedVmMigration,
selectedTimeRangeVmMigrations,
),
failed: toDataPointsForVmMigrations(
migrations
.map((migration) =>
migration?.status?.vms.filter((vm) =>
vm?.conditions?.find((it) => it?.type === 'Failed'),
),
)
.reduce((append, vm) => append.concat(vm), []),
toFinishedVmMigration,
selectedTimeRangeVmMigrations,
),
succeeded: toDataPointsForVmMigrations(
migrations
.map((migration) =>
migration?.status?.vms.filter((vm) =>
vm?.conditions?.find((it) => it?.type === 'Succeeded'),
),
)
.reduce((append, vm) => append.concat(vm), []),
toFinishedVmMigration,
selectedTimeRangeVmMigrations,
),
};

Expand All @@ -150,40 +213,46 @@ export const ChartsCard: React.FC<MigrationsCardProps> = () => {
...migrationsDataPoints.succeeded.map((m) => m.value),
);

const maxVmMigrationValue = Math.max(
...VmMigrationsDataPoints.running.map((m) => m.value),
...VmMigrationsDataPoints.failed.map((m) => m.value),
...VmMigrationsDataPoints.succeeded.map((m) => m.value),
);

return (
<Card>
<CardHeader>
<CardTitle className="forklift-title">
{t(TimeRangeOptionsDictionary[selectedTimeRange].labelKey)}
{t(TimeRangeOptionsDictionary[selectedTimeRangeMigrations].migrationsLabelKey)}
</CardTitle>
<CardActions>
<Dropdown
toggle={<KebabToggle onToggle={onToggle} />}
isOpen={isDropdownOpened}
toggle={<KebabToggle onToggle={onToggleMigrations} />}
isOpen={isMigrationsDropdownOpened}
isPlain
dropdownItems={[
<DropdownItem
onClick={() => {
onToggle();
setSelectedTimeRange(TimeRangeOptions.Last7Days);
onToggleMigrations();
setSelectedTimeRangeMigrations(TimeRangeOptions.Last7Days);
}}
key="7days"
>
{t('7 days')}
</DropdownItem>,
<DropdownItem
onClick={() => {
onToggle();
setSelectedTimeRange(TimeRangeOptions.Last31Days);
onToggleMigrations();
setSelectedTimeRangeMigrations(TimeRangeOptions.Last31Days);
}}
key="31days"
>
{t('31 days')}
</DropdownItem>,
<DropdownItem
onClick={() => {
onToggle();
setSelectedTimeRange(TimeRangeOptions.Last24H);
onToggleMigrations();
setSelectedTimeRangeMigrations(TimeRangeOptions.Last24H);
}}
key="24hours"
>
Expand Down Expand Up @@ -253,6 +322,108 @@ export const ChartsCard: React.FC<MigrationsCardProps> = () => {
</Chart>
</Flex>
</CardBody>

<CardHeader>
<CardTitle className="forklift-title">
{t(TimeRangeOptionsDictionary[selectedTimeRangeVmMigrations].vmMigrationsLabelKey)}
</CardTitle>
<CardActions>
<Dropdown
toggle={<KebabToggle onToggle={onToggleVmMigrations} />}
isOpen={isVmMigrationsDropdownOpened}
isPlain
dropdownItems={[
<DropdownItem
onClick={() => {
onToggleVmMigrations();
setSelectedTimeRangeVmMigrations(TimeRangeOptions.Last7Days);
}}
key="7days"
>
{t('7 days')}
</DropdownItem>,
<DropdownItem
onClick={() => {
onToggleVmMigrations();
setSelectedTimeRangeVmMigrations(TimeRangeOptions.Last31Days);
}}
key="31days"
>
{t('31 days')}
</DropdownItem>,
<DropdownItem
onClick={() => {
onToggleVmMigrations();
setSelectedTimeRangeVmMigrations(TimeRangeOptions.Last24H);
}}
key="24hours"
>
{t('24 hours')}
</DropdownItem>,
]}
/>
</CardActions>
</CardHeader>
<CardBody className="forklift-status-migration-chart">
<Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
<Chart
ariaDesc="Bar chart with VM migration statistics"
colorScale={[
chart_color_blue_200.var,
chart_color_red_100.var,
chart_color_green_400.var,
]}
domainPadding={{ x: [30, 25] }}
maxDomain={{ y: maxVmMigrationValue ? undefined : 5 }}
legendData={[
{ name: t('Running'), symbol: { fill: chart_color_blue_200.var } },
{ name: t('Failed'), symbol: { fill: chart_color_red_100.var } },
{ name: t('Succeeded'), symbol: { fill: chart_color_green_400.var } },
]}
legendPosition="bottom"
height={400}
width={1100}
padding={{
bottom: 75,
left: 50,
right: 30,
top: 50,
}}
>
<ChartAxis />
<ChartAxis dependentAxis showGrid />
<ChartGroup offset={11} horizontal={false}>
<ChartBar
data={VmMigrationsDataPoints.running.map(({ dateLabel, value }) => ({
x: dateLabel,
y: value,
name: t('Running'),
label: t('{{dateLabel}} Running: {{value}}', { dateLabel, value }),
}))}
labelComponent={<ChartTooltip constrainToVisibleArea />}
/>
<ChartBar
data={VmMigrationsDataPoints.failed.map(({ dateLabel, value }) => ({
x: dateLabel,
y: value,
name: t('Failed'),
label: t('{{dateLabel}} Failed: {{value}}', { dateLabel, value }),
}))}
labelComponent={<ChartTooltip constrainToVisibleArea />}
/>
<ChartBar
data={VmMigrationsDataPoints.succeeded.map(({ dateLabel, value }) => ({
x: dateLabel,
y: value,
name: 'Succeeded',
label: t('{{dateLabel}} Succeeded: {{value}}', { dateLabel, value }),
}))}
labelComponent={<ChartTooltip constrainToVisibleArea />}
/>
</ChartGroup>
</Chart>
</Flex>
</CardBody>
</Card>
);
};

0 comments on commit d3c6789

Please sign in to comment.