Skip to content

Commit

Permalink
Merge pull request kubevirt-ui#2149 from metalice/CNV-46825-change-it-1
Browse files Browse the repository at this point in the history
CNV-46825: Editing Instancetype series and size
  • Loading branch information
openshift-merge-bot[bot] authored Sep 2, 2024
2 parents e3d7b18 + 24151f4 commit 53fbaa3
Show file tree
Hide file tree
Showing 13 changed files with 360 additions and 34 deletions.
3 changes: 2 additions & 1 deletion locales/en/plugin__kubevirt-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,6 @@
"Could not create persistent volume claim": "Could not create persistent volume claim",
"CPU": "CPU",
"CPU | Memory": "CPU | Memory",
"CPU and Memory can not be edited if the VirtualMachine is created from InstanceType": "CPU and Memory can not be edited if the VirtualMachine is created from InstanceType",
"CPU used": "CPU used",
"CPUs": "CPUs",
"CPUs = sockets x threads x cores.": "CPUs = sockets x threads x cores.",
Expand Down Expand Up @@ -464,6 +463,7 @@
"Edit disk": "Edit disk",
"Edit Display name": "Edit Display name",
"Edit hostname": "Edit hostname",
"Edit Instancetype": "Edit Instancetype",
"Edit labels": "Edit labels",
"Edit Labels": "Edit Labels",
"Edit MigrationPolicy": "Edit MigrationPolicy",
Expand Down Expand Up @@ -1085,6 +1085,7 @@
"Send key": "Send key",
"Serial console": "Serial console",
"Serial Number": "Serial Number",
"Series": "Series",
"Server": "Server",
"Service Accounts": "Service Accounts",
"Services": "Services",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type CPUTopologyHelperTextProps = {

const CPUTopologyHelperText: FC<CPUTopologyHelperTextProps> = ({ cpu }) => {
const { t } = useKubevirtTranslation();
const { cores, sockets, threads } = cpu;
const { cores, sockets, threads } = cpu || {};

const totalCPU = cores * sockets * threads;

Expand Down
122 changes: 122 additions & 0 deletions src/utils/components/InstanceTypeModal/InstanceTypeModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React, { FC, useMemo, useState } from 'react';

import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { Flex, FlexItem, SelectList, SelectOption, Text } from '@patternfly/react-core';
import { InstanceTypeUnion } from '@virtualmachines/details/tabs/configuration/utils/types';

import FormPFSelect from '../FormPFSelect/FormPFSelect';
import TabModal from '../TabModal/TabModal';

import {
getInstanceTypeFromSeriesAndSize,
getInstanceTypeSeriesAndSize,
getInstanceTypeSeriesDisplayName,
getInstanceTypesPrettyDisplaySize,
getInstanceTypesSizes,
mappedInstanceTypesToSelectOptions,
} from './utils/util';

type InstanceTypeModalProps = {
allInstanceTypes: InstanceTypeUnion[];
instanceType: InstanceTypeUnion;
instanceTypeVM: V1VirtualMachine;
isOpen: boolean;
onClose: () => void;
onSubmit: (
updatedVM: V1VirtualMachine,
instanceType: InstanceTypeUnion,
) => Promise<V1VirtualMachine>;
};

const InstanceTypeModal: FC<InstanceTypeModalProps> = ({
allInstanceTypes,
instanceType,
instanceTypeVM,
isOpen,
onClose,
onSubmit,
}) => {
const { t } = useKubevirtTranslation();
const mappedInstanceTypes = useMemo(
() => mappedInstanceTypesToSelectOptions(allInstanceTypes),
[allInstanceTypes],
);
const { series: instanceTypeSeries, size: instanceTypeSize } = useMemo(
() => getInstanceTypeSeriesAndSize(instanceType),
[instanceType],
);

const [series, setSeries] = useState<string>(
getInstanceTypeSeriesDisplayName(mappedInstanceTypes, instanceTypeSeries),
);

const [size, setSize] = useState<string>(
getInstanceTypesPrettyDisplaySize(mappedInstanceTypes, instanceTypeSeries, instanceTypeSize),
);

const handleSubmit = (selectedInstanceType: InstanceTypeUnion) =>
onSubmit(instanceTypeVM, selectedInstanceType);

return (
<TabModal
headerText={t('Edit Instancetype')}
isOpen={isOpen}
obj={getInstanceTypeFromSeriesAndSize(mappedInstanceTypes, series, size)}
onClose={onClose}
onSubmit={handleSubmit}
>
<Flex
spaceItems={{
default: 'spaceItemsXl',
}}
direction={{ default: 'column' }}
spacer={{ default: 'spacer4xl' }}
>
<FlexItem>
<Text component="h5">{t('Series')}</Text>
<FormPFSelect
onSelect={(_, value) => {
if (value !== series) {
setSeries(value as string);
setSize(null);
}
}}
selected={series}
toggleProps={{ isFullWidth: true }}
>
<SelectList>
{Object.entries(mappedInstanceTypes).map(([key, value]) => (
<SelectOption
description={value.descriptionSeries}
key={key}
value={value.displayNameSeries}
>
{value.displayNameSeries}
</SelectOption>
))}
</SelectList>
</FormPFSelect>
</FlexItem>
<FlexItem>
<Text component="h5">{t('Size')}</Text>
<FormPFSelect
onSelect={(_, value) => {
setSize(value as string);
}}
selected={size}
toggleProps={{ isFullWidth: true }}
>
{getInstanceTypesSizes(mappedInstanceTypes, series)?.map((item) => (
<SelectOption key={item.prettyDisplaySize} value={item.prettyDisplaySize}>
{item?.prettyDisplaySize}
</SelectOption>
))}
</FormPFSelect>
</FlexItem>
</Flex>
</TabModal>
);
};

export default InstanceTypeModal;
37 changes: 37 additions & 0 deletions src/utils/components/InstanceTypeModal/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { InstanceTypeUnion } from '@virtualmachines/details/tabs/configuration/utils/types';

export type InstanceTypesSeries =
| 'cx1'
| 'gn1'
| 'highperformance'
| 'm1'
| 'n1'
| 'o1'
| 'rt1'
| 'u1';

export type InstanceTypesSizes =
| '2xlarge'
| '4xlarge'
| '8xlarge'
| 'large'
| 'medium'
| 'micro'
| 'nano'
| 'small'
| 'xlarge';

export type MappedInstanceTypes = Record<
InstanceTypesSeries,
{
sizes: {
[key in InstanceTypesSizes]?: {
instanceType: InstanceTypeUnion;
prettyDisplaySize: string;
series: string;
seriesDisplayName: string;
size: string;
};
};
} & { descriptionSeries?: string; displayNameSeries?: string }
>;
92 changes: 92 additions & 0 deletions src/utils/components/InstanceTypeModal/utils/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { InstanceTypeSize } from '@catalog/CreateFromInstanceTypes/components/SelectInstanceTypeSection/utils/types';
import {
INSTANCETYPE_CLASS_DISPLAY_NAME,
INSTANCETYPE_DESCRIPTION_ANNOTATION,
REDHAT_COM,
} from '@kubevirt-utils/components/AddBootableVolumeModal/components/VolumeMetadata/components/InstanceTypeDrilldownSelect/utils/constants';
import { VENDOR_LABEL } from '@kubevirt-utils/constants/constants';
import { t } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { getAnnotation, getLabel } from '@kubevirt-utils/resources/shared';
import { InstanceTypeUnion } from '@virtualmachines/details/tabs/configuration/utils/types';

import { InstanceTypesSeries, InstanceTypesSizes, MappedInstanceTypes } from './types';

export const getInstanceTypeItemSizePrettyDisplay = (it: InstanceTypeUnion): string =>
`${it?.metadata.name.split('.').pop()}: ${it?.spec?.cpu?.guest} ${t('CPUs')}, ${
it?.spec?.memory?.guest
} ${t('Memory')}`;

export const getInstanceTypeClassDisplayAnnotation = (instanceType: InstanceTypeUnion): string => {
return getAnnotation(instanceType, INSTANCETYPE_CLASS_DISPLAY_NAME);
};

export const getInstanceTypeDescriptionAnnotation = (instanceType: InstanceTypeUnion): string => {
return getAnnotation(instanceType, INSTANCETYPE_DESCRIPTION_ANNOTATION);
};

export const getInstanceTypeSeriesAndSize = (
instanceType: InstanceTypeUnion,
): { series: InstanceTypesSeries; size: InstanceTypesSizes } => {
const [series, size] = instanceType?.metadata?.name?.split('.');
return { series: series as InstanceTypesSeries, size: size as InstanceTypesSizes };
};

export const mappedInstanceTypesToSelectOptions = (
instanceTypes: InstanceTypeUnion[],
): MappedInstanceTypes =>
instanceTypes.reduce((acc, it) => {
if (getLabel(it, VENDOR_LABEL) === REDHAT_COM) {
const { series, size } = getInstanceTypeSeriesAndSize(it);
acc[series] = {
...(acc[series] || {}),
descriptionSeries: getInstanceTypeDescriptionAnnotation(it),
displayNameSeries: getInstanceTypeClassDisplayAnnotation(it),
sizes: {
...(acc?.[series]?.sizes || {}),
[size]: {
instanceType: it,
prettyDisplaySize: getInstanceTypeItemSizePrettyDisplay(it),
series,
seriesDisplayName: getInstanceTypeClassDisplayAnnotation(it),
size,
},
},
};
}
return acc;
}, {} as MappedInstanceTypes);

export const getInstanceTypesPrettyDisplaySize = (
mappedInstanceTypes: MappedInstanceTypes,
instanceTypeSeries: InstanceTypesSeries,
instanceTypeSize: InstanceTypeSize,
) => mappedInstanceTypes?.[instanceTypeSeries]?.sizes[instanceTypeSize]?.prettyDisplaySize;

export const getInstanceTypesSizes = (mappedInstanceTypes: MappedInstanceTypes, series: string) => {
const matchedSeries = Object.values(mappedInstanceTypes).find(
(it) => it.displayNameSeries === series,
);
return Object.values(matchedSeries?.sizes);
};

export const getInstanceTypeSeriesDisplayName = (
mappedInstanceTypes: MappedInstanceTypes,
instanceTypeSeries: InstanceTypesSeries,
) => mappedInstanceTypes?.[instanceTypeSeries]?.displayNameSeries;

export const getInstanceTypeFromSeriesAndSize = (
mappedInstanceTypes: MappedInstanceTypes,
instanceTypeSeries: string,
instanceTypeSize: string,
): InstanceTypeUnion => {
const instanceTypesSeries = Object.values(mappedInstanceTypes);

const matchedSeries = instanceTypesSeries.find(
(series) => series.displayNameSeries === instanceTypeSeries,
);
const matchedSize = Object.values(matchedSeries?.sizes).find(
(size) => size.prettyDisplaySize === instanceTypeSize,
);

return matchedSize?.instanceType;
};
3 changes: 2 additions & 1 deletion src/utils/components/TabModal/TabModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { ComponentType, memo, MouseEventHandler, ReactNode, useState } from 'react';

import { V1VirtualMachine } from '@kubevirt-ui/kubevirt-api/kubevirt';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { kubevirtConsole } from '@kubevirt-utils/utils/utils';
import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk';
Expand Down Expand Up @@ -29,7 +30,7 @@ export type TabModalProps<T extends K8sResourceCommon = K8sResourceCommon> = {
modalVariant?: ModalVariant;
obj?: T;
onClose: () => Promise<void> | void;
onSubmit: (obj: T) => Promise<string | T | T[] | void>;
onSubmit: (obj: T) => Promise<string | T | T[] | V1VirtualMachine | void>;
positionTop?: boolean;
submitBtnText?: string;
submitBtnVariant?: ButtonVariant;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type VirtualMachineDescriptionItemProps = {
moreInfoURL?: string;
onEditClick?: () => void;
showEditOnTitle?: boolean;
subTitle?: string;
};

const VirtualMachineDescriptionItem: FC<VirtualMachineDescriptionItemProps> = ({
Expand All @@ -51,6 +52,7 @@ const VirtualMachineDescriptionItem: FC<VirtualMachineDescriptionItemProps> = ({
moreInfoURL,
onEditClick,
showEditOnTitle,
subTitle,
}) => {
const { t } = useKubevirtTranslation();
const NotAvailable = <MutedTextSpan text={t('Not available')} />;
Expand Down Expand Up @@ -108,6 +110,7 @@ const VirtualMachineDescriptionItem: FC<VirtualMachineDescriptionItemProps> = ({
className="pf-c-description-list__description"
data-test-id={testId}
>
{subTitle && <div className="pf-c-description-list__text pf-v5-u-my-sm">{subTitle}</div>}
{isEdit && !showEditOnTitle ? description : descriptionData}
</DescriptionListDescription>
</DescriptionListGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { FC, useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom-v5-compat';
import { useLocation } from 'react-router-dom-v5-compat';

import useInstanceTypesAndPreferences from '@catalog/CreateFromInstanceTypes/state/hooks/useInstanceTypesAndPreferences';
import GuidedTour from '@kubevirt-utils/components/GuidedTour/GuidedTour';
import {
runningTourSignal,
Expand All @@ -27,6 +28,7 @@ const VirtualMachineConfigurationTab: FC<NavPageComponentProps> = ({ obj }) => {

const vm = runningTourSignal.value ? tourGuideVM : obj;
const { vmi } = useVMI(getName(vm), getNamespace(vm));
const { allInstanceTypes } = useInstanceTypesAndPreferences();
const [instanceTypeVM] = useInstanceTypeExpandSpec(vm);
const [activeTabKey, setActiveTabKey] = useState<number | string>(
VirtualMachineDetailsTab.Details,
Expand Down Expand Up @@ -63,7 +65,12 @@ const VirtualMachineConfigurationTab: FC<NavPageComponentProps> = ({ obj }) => {
title={<TabTitleText>{title}</TabTitleText>}
>
{activeTabKey === name && (
<Component instanceTypeVM={instanceTypeVM} vm={vm} vmi={vmi} />
<Component
allInstanceTypes={allInstanceTypes}
instanceTypeVM={instanceTypeVM}
vm={vm}
vmi={vmi}
/>
)}
</Tab>
))}
Expand Down
Loading

0 comments on commit 53fbaa3

Please sign in to comment.