Skip to content

Commit

Permalink
Merge pull request #2231 from upalatucci/init-migrate
Browse files Browse the repository at this point in the history
CNV-36070: Create initial migration interface
  • Loading branch information
openshift-merge-bot[bot] authored Oct 21, 2024
2 parents f0109fb + 091f8ad commit 7ced6c9
Show file tree
Hide file tree
Showing 14 changed files with 590 additions and 50 deletions.
10 changes: 9 additions & 1 deletion locales/en/plugin__kubevirt-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@
"Clone template": "Clone template",
"Clone volume": "Clone volume",
"Cloning": "Cloning",
"Close header": "Close header",
"Closing it will cause the upload to fail. You may still navigate the console.": "Closing it will cause the upload to fail. You may still navigate the console.",
"Cloud-init": "Cloud-init",
"Cloud-init and SSH key configurations will be applied to the VirtualMachine only at the first boot.": "Cloud-init and SSH key configurations will be applied to the VirtualMachine only at the first boot.",
Expand All @@ -277,6 +278,7 @@
"Completion time": "Completion time",
"Completion timeout": "Completion timeout",
"CompletionTimeoutPerGiB is the maximum number of seconds per GiB a migration is allowed to take. If a live-migration takes longer to migrate than this value multiplied by the size of the VMI, the migration will be cancelled, unless AllowPostCopy is true. Defaults to 800. ": "CompletionTimeoutPerGiB is the maximum number of seconds per GiB a migration is allowed to take. If a live-migration takes longer to migrate than this value multiplied by the size of the VMI, the migration will be cancelled, unless AllowPostCopy is true. Defaults to 800. ",
"Compute": "Compute",
"Compute-intensive applications": "Compute-intensive applications",
"Condition": "Condition",
"Conditions": "Conditions",
Expand Down Expand Up @@ -411,6 +413,7 @@
"Desktop viewer": "Desktop viewer",
"Destination details": "Destination details",
"Destination project": "Destination project",
"Destination StorageClass": "Destination StorageClass",
"Detach": "Detach",
"Detach disk?": "Detach disk?",
"Detach sysprep": "Detach sysprep",
Expand Down Expand Up @@ -720,10 +723,12 @@
"Metrics": "Metrics",
"Metrics are collected by the OpenShift Monitoring Operator.": "Metrics are collected by the OpenShift Monitoring Operator.",
"Migratable": "Migratable",
"Migrate": "Migrate",
"Migrate a VirtualMachine to a different Node or change the selected time range.": "Migrate a VirtualMachine to a different Node or change the selected time range.",
"Migrate multiple virtual machine workloads to OpenShift Virtualization. ": "Migrate multiple virtual machine workloads to OpenShift Virtualization. ",
"Migrate to a different Node": "Migrate to a different Node",
"Migrate VirtualMachine storage": "Migrate VirtualMachine storage",
"Migrate VirtualMachine storage to a different StorageClass": "Migrate VirtualMachine storage to a different StorageClass",
"Migrate VirtualMachine storage to a different StorageClass.": "Migrate VirtualMachine storage to a different StorageClass.",
"Migration": "Migration",
"Migration chart": "Migration chart",
"Migration metrics": "Migration metrics",
Expand Down Expand Up @@ -1003,6 +1008,7 @@
"Restore the VirtualMachine to this snapshot`s state": "Restore the VirtualMachine to this snapshot`s state",
"Restore VirtualMachine from snapshot": "Restore VirtualMachine from snapshot",
"Retain revisions": "Retain revisions",
"Review": "Review",
"RHEL download page ": "RHEL download page ",
"Rules with \"Preferred\" condition will stack with an \"AND\" relation between them.": "Rules with \"Preferred\" condition will stack with an \"AND\" relation between them.",
"Rules with \"Required\" condition will stack with an \"OR\" relation between them.": "Rules with \"Required\" condition will stack with an \"OR\" relation between them.",
Expand Down Expand Up @@ -1071,6 +1077,7 @@
"Select storage class": "Select storage class",
"Select StorageClass": "Select StorageClass",
"Select target node": "Select target node",
"Select the destination storage for the VirtualMachine storage migration.": "Select the destination storage for the VirtualMachine storage migration.",
"Select this option if you use an on-premise subscription service": "Select this option if you use an on-premise subscription service",
"Select volume to boot from": "Select volume to boot from",
"Select VolumeSnapshot": "Select VolumeSnapshot",
Expand Down Expand Up @@ -1340,6 +1347,7 @@
"vCPU": "vCPU",
"vCPU wait": "vCPU wait",
"Vendor": "Vendor",
"Verify the details and click <1>Migrate VirtualMachine storage</1> to start the migration": "Verify the details and click <1>Migrate VirtualMachine storage</1> to start the migration",
"view {{qualifiedNodesCount}} matching nodes": "view {{qualifiedNodesCount}} matching nodes",
"View alert": "View alert",
"View alerts": "View alerts",
Expand Down
31 changes: 24 additions & 7 deletions src/utils/components/ActionDropdownItem/ActionDropdownItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ import React, { Dispatch, FC, SetStateAction } from 'react';
import classNames from 'classnames';

import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { Action, useAccessReview } from '@openshift-console/dynamic-plugin-sdk';
import { DropdownItem, TooltipPosition } from '@patternfly/react-core';
import { useAccessReview } from '@openshift-console/dynamic-plugin-sdk';
import { Menu, MenuContent, MenuItem, MenuList, TooltipPosition } from '@patternfly/react-core';

import { ActionDropdownItemType } from '../ActionsDropdown/constants';

import './action-dropdown-item.scss';

type ActionDropdownItemProps = {
action: Action;
action: ActionDropdownItemType;
setIsOpen: Dispatch<SetStateAction<boolean>>;
};

const ActionDropdownItem: FC<ActionDropdownItemProps> = ({ action, setIsOpen }) => {
const { t } = useKubevirtTranslation();
const [actionAllowed] = useAccessReview(action?.accessReview);
const [actionAllowed] = useAccessReview(action?.accessReview || {});
const isCloneDisabled = !actionAllowed && action?.id === 'vm-action-clone';

const handleClick = () => {
Expand All @@ -25,7 +27,7 @@ const ActionDropdownItem: FC<ActionDropdownItemProps> = ({ action, setIsOpen })
};

return (
<DropdownItem
<MenuItem
data-test-id={`${action?.id}`}
description={action?.description}
isDisabled={action?.disabled || !actionAllowed}
Expand All @@ -37,7 +39,22 @@ const ActionDropdownItem: FC<ActionDropdownItemProps> = ({ action, setIsOpen })
position: TooltipPosition.left,
},
})}
className={classNames({ ActionDropdownItem__disabled: isCloneDisabled })}
className={classNames('ActionDropdownItem', {
ActionDropdownItem__disabled: isCloneDisabled,
})}
flyoutMenu={
action?.options && (
<Menu containsFlyout id={`menu-${action.id}`}>
<MenuContent>
<MenuList>
{action?.options?.map((option) => (
<ActionDropdownItem action={option} key={option.id} setIsOpen={setIsOpen} />
))}
</MenuList>
</MenuContent>
</Menu>
)
}
>
{action?.label}
{action?.icon && (
Expand All @@ -46,7 +63,7 @@ const ActionDropdownItem: FC<ActionDropdownItemProps> = ({ action, setIsOpen })
<span className="text-muted">{action.icon}</span>
</>
)}
</DropdownItem>
</MenuItem>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
cursor: unset;
color: var(--pf-global--disabled-color--100);
}

.pf-v5-c-menu.pf-m-flyout {
--pf-v5-c-menu--Width: 200px;
}
}
53 changes: 32 additions & 21 deletions src/utils/components/ActionsDropdown/ActionsDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import React, { FC, memo, useState } from 'react';
import React, { FC, memo, useRef, useState } from 'react';

import ActionDropdownItem from '@kubevirt-utils/components/ActionDropdownItem/ActionDropdownItem';
import DropdownToggle from '@kubevirt-utils/components/toggles/DropdownToggle';
import KebabToggle from '@kubevirt-utils/components/toggles/KebabToggle';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { Action } from '@openshift-console/dynamic-plugin-sdk';
import { Dropdown, DropdownList } from '@patternfly/react-core';
import { Menu, MenuContent, MenuList, Popper } from '@patternfly/react-core';

import ActionDropdownItem from '../ActionDropdownItem/ActionDropdownItem';

import { ActionDropdownItemType } from './constants';

type ActionsDropdownProps = {
actions: Action[];
actions: ActionDropdownItemType[];
className?: string;
id?: string;
isKebabToggle?: boolean;
Expand All @@ -17,13 +19,14 @@ type ActionsDropdownProps = {

const ActionsDropdown: FC<ActionsDropdownProps> = ({
actions = [],
className,
id,
isKebabToggle,
onLazyClick,
}) => {
const { t } = useKubevirtTranslation();
const [isOpen, setIsOpen] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
const toggleRef = useRef<HTMLButtonElement>(null);
const containerRef = useRef<HTMLDivElement>(null);

const onToggle = () => {
setIsOpen((prevIsOpen) => {
Expand All @@ -41,21 +44,29 @@ const ActionsDropdown: FC<ActionsDropdownProps> = ({
onClick: onToggle,
});

const menu = (
<Menu containsFlyout ref={menuRef}>
<MenuContent>
<MenuList>
{actions?.map((action) => (
<ActionDropdownItem action={action} key={action?.id} setIsOpen={setIsOpen} />
))}
</MenuList>
</MenuContent>
</Menu>
);

return (
<Dropdown
className={className}
data-test-id={id}
isOpen={isOpen}
onOpenChange={(open: boolean) => setIsOpen(open)}
popperProps={{ enableFlip: true, position: 'right' }}
toggle={Toggle}
>
<DropdownList>
{actions?.map((action) => (
<ActionDropdownItem action={action} key={action?.id} setIsOpen={setIsOpen} />
))}
</DropdownList>
</Dropdown>
<div className="kv-actions-dropdown" ref={containerRef}>
{Toggle(toggleRef)}
<Popper
appendTo={containerRef.current}
isVisible={isOpen}
placement="bottom-end"
popper={menu}
triggerRef={toggleRef}
/>
</div>
);
};

Expand Down
5 changes: 5 additions & 0 deletions src/utils/components/ActionsDropdown/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Action } from '@openshift-console/dynamic-plugin-sdk';

type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;

export type ActionDropdownItemType = Optional<Action, 'cta'> & { options?: Action[] };
43 changes: 38 additions & 5 deletions src/views/virtualmachines/actions/VirtualMachineActionFactory.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { Location, NavigateFunction } from 'react-router-dom-v5-compat';

import VirtualMachineCloneModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineCloneModel';
import VirtualMachineInstanceMigrationModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineInstanceMigrationModel';
Expand All @@ -8,13 +9,14 @@ import {
V1VirtualMachine,
V1VirtualMachineInstanceMigration,
} from '@kubevirt-ui/kubevirt-api/kubevirt';
import { ActionDropdownItemType } from '@kubevirt-utils/components/ActionsDropdown/constants';
import { AnnotationsModal } from '@kubevirt-utils/components/AnnotationsModal/AnnotationsModal';
import CloneVMModal from '@kubevirt-utils/components/CloneVMModal/CloneVMModal';
import { LabelsModal } from '@kubevirt-utils/components/LabelsModal/LabelsModal';
import { ModalComponent } from '@kubevirt-utils/components/ModalProvider/ModalProvider';
import SnapshotModal from '@kubevirt-utils/components/SnapshotModal/SnapshotModal';
import { t } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { asAccessReview } from '@kubevirt-utils/resources/shared';
import { asAccessReview, getNamespace, getResourceUrl } from '@kubevirt-utils/resources/shared';
import { getVMSSHSecretName } from '@kubevirt-utils/resources/vm';
import { isEmpty } from '@kubevirt-utils/utils/utils';
import { Action, k8sPatch } from '@openshift-console/dynamic-plugin-sdk';
Expand Down Expand Up @@ -46,7 +48,7 @@ const {
} = printableVMStatus;

export const VirtualMachineActionFactory = {
cancelMigration: (
cancelMigrationCompute: (
vm: V1VirtualMachine,
vmim: V1VirtualMachineInstanceMigration,
isSingleNodeCluster: boolean,
Expand Down Expand Up @@ -99,7 +101,6 @@ export const VirtualMachineActionFactory = {
label: t('Delete'),
};
},

editAnnotations: (vm: V1VirtualMachine, createModal: (modal: ModalComponent) => void): Action => {
return {
accessReview: asAccessReview(VirtualMachineModel, vm, 'patch'),
Expand Down Expand Up @@ -129,6 +130,7 @@ export const VirtualMachineActionFactory = {
label: t('Edit annotations'),
};
},

editLabels: (vm: V1VirtualMachine, createModal: (modal: ModalComponent) => void): Action => {
return {
accessReview: asAccessReview(VirtualMachineModel, vm, 'patch'),
Expand Down Expand Up @@ -170,7 +172,7 @@ export const VirtualMachineActionFactory = {
label: t('Force stop'),
};
},
migrate: (vm: V1VirtualMachine, isSingleNodeCluster: boolean): Action => {
migrateCompute: (vm: V1VirtualMachine, isSingleNodeCluster: boolean): Action => {
return {
accessReview: {
group: VirtualMachineInstanceMigrationModel.apiGroup,
Expand All @@ -182,9 +184,40 @@ export const VirtualMachineActionFactory = {
description: t('Migrate to a different Node'),
disabled: !isLiveMigratable(vm, isSingleNodeCluster),
id: 'vm-action-migrate',
label: t('Migrate'),
label: t('Compute'),
};
},
migrateStorage: (
vm: V1VirtualMachine,
navigate: NavigateFunction,
location: Location,
): Action => {
return {
accessReview: {
group: VirtualMachineModel.apiGroup,
namespace: getNamespace(vm),
resource: VirtualMachineModel.plural,
verb: 'patch',
},
cta: () =>
navigate(
`${getResourceUrl({
model: VirtualMachineModel,
resource: vm,
})}/migratestorage?fromURL=${encodeURIComponent(
`${location.pathname}${location.search}`,
)}`,
),
description: t('Migrate VirtualMachine storage to a different StorageClass'),
id: 'vm-migrate-storage',
label: t('Storage'),
};
},
migrationActions: (...migrationActions): ActionDropdownItemType => ({
id: 'migration-menu',
label: 'Migration',
options: migrationActions,
}),
pause: (vm: V1VirtualMachine): Action => {
return {
accessReview: asAccessReview(VirtualMachineModel, vm, 'patch'),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { FC, memo } from 'react';

import ActionsDropdown from '@kubevirt-utils/components/ActionsDropdown/ActionsDropdown';
import { Action } from '@openshift-console/dynamic-plugin-sdk';
import { ActionDropdownItemType } from '@kubevirt-utils/components/ActionsDropdown/constants';

import './VirtualMachineActions.scss';

type VirtualMachinesInstanceActionsProps = {
actions: Action[];
actions: ActionDropdownItemType[];
isKebabToggle?: boolean;
};

Expand Down
Loading

0 comments on commit 7ced6c9

Please sign in to comment.