From 0eafaf85b2a3069fe073894b1ceb4748f0952791 Mon Sep 17 00:00:00 2001 From: Joachim Schuler Date: Tue, 14 Jan 2025 14:59:26 -0500 Subject: [PATCH] Allow plan mappings to be modified even if a VM has migrated Signed-off-by: Joachim Schuler --- .../en/plugin__forklift-console-plugin.json | 6 +-- .../ProvidersVirtualMachinesList.tsx | 13 +++++- .../MemoizedProviderVirtualMachinesList.tsx | 3 ++ .../SelectSourceProvider.tsx | 3 ++ .../views/details/components/MappingList.tsx | 10 ++++- .../details/components/MappingListItem.tsx | 40 ++++++++++++++----- .../details/tabs/Mappings/PlanMappings.tsx | 25 ++++++++++++ .../tabs/Mappings/PlanMappingsSection.tsx | 29 ++++++++++++-- .../MigrationVirtualMachinesList.tsx | 18 ++++----- .../components/PlanVMsEditButton.tsx | 15 ++----- .../modules/Plans/views/edit/PlanEditPage.tsx | 10 +++++ .../OVirtVirtualMachinesList.tsx | 2 + .../OpenShiftVirtualMachinesList.tsx | 2 + .../OpenStackVirtualMachinesList.tsx | 2 + .../OvaVirtualMachinesList.tsx | 2 + .../ProviderVirtualMachines.tsx | 7 ++++ .../VSphereVirtualMachinesList.tsx | 2 + .../ProviderVirtualMachinesList.tsx | 3 ++ .../views/migrate/useFetchEffects.ts | 1 + 19 files changed, 154 insertions(+), 39 deletions(-) diff --git a/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json b/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json index 20bd676e6..cbcabfffd 100644 --- a/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json +++ b/packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json @@ -487,9 +487,8 @@ "The certificate is not a valid PEM-encoded X.509 certificate": "The certificate is not a valid PEM-encoded X.509 certificate", "The chosen provider is no longer available.": "The chosen provider is no longer available.", "The current certificate does not match the certificate fetched from URL. Manually validate the fingerprint before proceeding.": "The current certificate does not match the certificate fetched from URL. Manually validate the fingerprint before proceeding.", - "The edit mappings button is disabled if the plan started running and at least one virtual machine was migrated successfully or when the plan status does not enable editing.": "The edit mappings button is disabled if the plan started running and at least one virtual machine was migrated successfully or when the plan status does not enable editing.", - "The edit virtual machines button is disabled if the plan started running and at least one virtual machine was migrated successfully.": "The edit virtual machines button is disabled if the plan started running and at least one virtual machine was migrated successfully.", - "The edit virtual machines button is disabled while the plan is not editable.": "The edit virtual machines button is disabled while the plan is not editable.", + "The edit mappings button is disabled when the plan status does not enable editing.": "The edit mappings button is disabled when the plan status does not enable editing.", + "The edit virtual machines button is disabled when the plan status does not enable editing.": "The edit virtual machines button is disabled when the plan status does not enable editing.", "The interval in minutes for precopy. Default value is 60.": "The interval in minutes for precopy. Default value is 60.", "The interval in seconds for snapshot pooling. Default value is 10.": "The interval in seconds for snapshot pooling. Default value is 10.", "The limit for CPU usage by the controller, specified in milliCPU. Default value is 500m.": "The limit for CPU usage by the controller, specified in milliCPU. Default value is 500m.", @@ -507,6 +506,7 @@ "The URL of the Red Hat Virtualization Manager (RHVM) API endpoint, for example: https://rhv-host-example.com/ovirt-engine/api .": "The URL of the Red Hat Virtualization Manager (RHVM) API endpoint, for example: https://rhv-host-example.com/ovirt-engine/api .", "The URL of the vCenter API endpoint, for example: https://vCenter-host-example.com/sdk .": "The URL of the vCenter API endpoint, for example: https://vCenter-host-example.com/sdk .", "The username for the ESXi host admin": "The username for the ESXi host admin", + "This mapping's associated VM has been migrated and can no longer be modified.": "This mapping's associated VM has been migrated and can no longer be modified.", "To troubleshoot, check the Forklift controller pod events and logs.": "To troubleshoot, check the Forklift controller pod events and logs.", "To troubleshoot, check the Forklift controller pod logs.": "To troubleshoot, check the Forklift controller pod logs.", "To troubleshoot, view the network map details page\n and check the Forklift controller pod logs.": "To troubleshoot, view the network map details page\n and check the Forklift controller pod logs.", diff --git a/packages/forklift-console-plugin/src/modules/Plans/views/create/components/ProvidersVirtualMachinesList.tsx b/packages/forklift-console-plugin/src/modules/Plans/views/create/components/ProvidersVirtualMachinesList.tsx index e1b730b7d..7b90240ed 100644 --- a/packages/forklift-console-plugin/src/modules/Plans/views/create/components/ProvidersVirtualMachinesList.tsx +++ b/packages/forklift-console-plugin/src/modules/Plans/views/create/components/ProvidersVirtualMachinesList.tsx @@ -14,9 +14,19 @@ export const ProviderVirtualMachinesList: React.FC<{ namespace: string; onSelect?: (selectedIds: VmData[]) => void; initialSelectedIds?: string[]; + disabledVmIds?: string[]; showActions: boolean; className?: string; -}> = ({ title, name, namespace, onSelect, initialSelectedIds, showActions, className }) => { +}> = ({ + title, + name, + namespace, + onSelect, + initialSelectedIds, + disabledVmIds, + showActions, + className, +}) => { const [provider, providerLoaded, providerLoadError] = useK8sWatchResource({ groupVersionKind: ProviderModelGroupVersionKind, namespaced: true, @@ -35,6 +45,7 @@ export const ProviderVirtualMachinesList: React.FC<{ loadError={providerLoadError} onSelect={onSelect} initialSelectedIds={initialSelectedIds} + disabledVmIds={disabledVmIds} showActions={showActions} className={className} /> diff --git a/packages/forklift-console-plugin/src/modules/Plans/views/create/steps/SelectSourceProvider/MemoizedProviderVirtualMachinesList.tsx b/packages/forklift-console-plugin/src/modules/Plans/views/create/steps/SelectSourceProvider/MemoizedProviderVirtualMachinesList.tsx index ef557cbcc..b95777059 100644 --- a/packages/forklift-console-plugin/src/modules/Plans/views/create/steps/SelectSourceProvider/MemoizedProviderVirtualMachinesList.tsx +++ b/packages/forklift-console-plugin/src/modules/Plans/views/create/steps/SelectSourceProvider/MemoizedProviderVirtualMachinesList.tsx @@ -9,6 +9,7 @@ export interface ProviderVirtualMachinesListProps { namespace: string; onSelect: (selectedVms: VmData[]) => void; initialSelectedIds: string[]; + disabledVmIds?: string[]; showActions: boolean; } @@ -19,6 +20,7 @@ export const MemoizedProviderVirtualMachinesList = memo( namespace, onSelect, initialSelectedIds, + disabledVmIds, showActions, }: ProviderVirtualMachinesListProps) => { return ( @@ -28,6 +30,7 @@ export const MemoizedProviderVirtualMachinesList = memo( namespace={namespace} onSelect={onSelect} initialSelectedIds={initialSelectedIds} + disabledVmIds={disabledVmIds} showActions={showActions} /> ); diff --git a/packages/forklift-console-plugin/src/modules/Plans/views/create/steps/SelectSourceProvider/SelectSourceProvider.tsx b/packages/forklift-console-plugin/src/modules/Plans/views/create/steps/SelectSourceProvider/SelectSourceProvider.tsx index f506c2030..4fd49afca 100644 --- a/packages/forklift-console-plugin/src/modules/Plans/views/create/steps/SelectSourceProvider/SelectSourceProvider.tsx +++ b/packages/forklift-console-plugin/src/modules/Plans/views/create/steps/SelectSourceProvider/SelectSourceProvider.tsx @@ -20,6 +20,7 @@ export const SelectSourceProvider: React.FC<{ dispatch: React.Dispatch>; filterDispatch: React.Dispatch; hideProviderSection?: boolean; + disabledVmIds?: string[]; }> = ({ filterState, providers, @@ -29,6 +30,7 @@ export const SelectSourceProvider: React.FC<{ dispatch, filterDispatch, hideProviderSection, + disabledVmIds, }) => { const { t } = useForkliftTranslation(); @@ -76,6 +78,7 @@ export const SelectSourceProvider: React.FC<{ filterDispatch({ type: 'UPDATE_SELECTED_VMS', payload: selectedVms }) } initialSelectedIds={filterState.selectedVMs.map((vm) => vm.vm.id)} + disabledVmIds={disabledVmIds} showActions={false} /> diff --git a/packages/forklift-console-plugin/src/modules/Plans/views/details/components/MappingList.tsx b/packages/forklift-console-plugin/src/modules/Plans/views/details/components/MappingList.tsx index 9fb6a436e..910c328d8 100644 --- a/packages/forklift-console-plugin/src/modules/Plans/views/details/components/MappingList.tsx +++ b/packages/forklift-console-plugin/src/modules/Plans/views/details/components/MappingList.tsx @@ -32,6 +32,13 @@ interface MappingListProps { * Is in edit/view mode? In case of view mode, the DataListCells are disabled and buttons are hidden */ isEditable?: boolean; + /** + * A function that determines whether a mapping list item is editable + * + * @param source The mapping source + * @returns boolean + */ + canEditItem?: (source: string) => boolean; } export const MappingList: FC = ({ @@ -45,9 +52,9 @@ export const MappingList: FC = ({ noSourcesLabel, isDisabled = false, isEditable = true, + canEditItem, }) => { const { t } = useForkliftTranslation(); - return ( <> @@ -64,6 +71,7 @@ export const MappingList: FC = ({ generalSourcesLabel={generalSourcesLabel} noSourcesLabel={noSourcesLabel} isEditable={isEditable} + canEditItem={(source) => canEditItem?.(source)} /> ))} diff --git a/packages/forklift-console-plugin/src/modules/Plans/views/details/components/MappingListItem.tsx b/packages/forklift-console-plugin/src/modules/Plans/views/details/components/MappingListItem.tsx index 15a231151..b210f2a3c 100644 --- a/packages/forklift-console-plugin/src/modules/Plans/views/details/components/MappingListItem.tsx +++ b/packages/forklift-console-plugin/src/modules/Plans/views/details/components/MappingListItem.tsx @@ -10,6 +10,7 @@ import { DataListItem, DataListItemCells, DataListItemRow, + Tooltip, } from '@patternfly/react-core'; import { Select, @@ -35,6 +36,7 @@ interface MappingListItemProps { replaceMapping: (val: { current: Mapping; next: Mapping }) => void; deleteMapping: (mapping: Mapping) => void; isEditable: boolean; + canEditItem?: (source: string) => boolean; } export const MappingListItem: FC = ({ @@ -48,11 +50,14 @@ export const MappingListItem: FC = ({ replaceMapping, deleteMapping, isEditable, + canEditItem, }) => { const { t } = useForkliftTranslation(); const [isSrcOpen, setToggleSrcOpen] = useToggle(false); const [isTrgOpen, setToggleTrgOpen] = useToggle(false); + const canEditMapping = canEditItem?.(source); + const onClick = () => { deleteMapping({ source, destination }); }; @@ -93,7 +98,7 @@ export const MappingListItem: FC = ({ onSelect={onSelectSource} selections={source} isOpen={isSrcOpen} - isDisabled={!isEditable} + isDisabled={!isEditable || !canEditMapping} aria-labelledby="" isGrouped menuAppendTo={() => document.body} @@ -112,7 +117,7 @@ export const MappingListItem: FC = ({ onSelect={onSelectDestination} selections={destination} isOpen={isTrgOpen} - isDisabled={!isEditable} + isDisabled={!isEditable || !canEditMapping} aria-labelledby="" menuAppendTo={() => document.body} > @@ -129,13 +134,30 @@ export const MappingListItem: FC = ({ aria-label={t('Actions')} aria-labelledby="" > -