Skip to content

Commit

Permalink
Allow plan mappings to be modified even if a VM has migrated
Browse files Browse the repository at this point in the history
Signed-off-by: Joachim Schuler <[email protected]>
  • Loading branch information
jschuler committed Jan 14, 2025
1 parent 4cf4327 commit 0eafaf8
Show file tree
Hide file tree
Showing 19 changed files with 154 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand All @@ -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.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<V1beta1Provider>({
groupVersionKind: ProviderModelGroupVersionKind,
namespaced: true,
Expand All @@ -35,6 +45,7 @@ export const ProviderVirtualMachinesList: React.FC<{
loadError={providerLoadError}
onSelect={onSelect}
initialSelectedIds={initialSelectedIds}
disabledVmIds={disabledVmIds}
showActions={showActions}
className={className}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface ProviderVirtualMachinesListProps {
namespace: string;
onSelect: (selectedVms: VmData[]) => void;
initialSelectedIds: string[];
disabledVmIds?: string[];
showActions: boolean;
}

Expand All @@ -19,6 +20,7 @@ export const MemoizedProviderVirtualMachinesList = memo(
namespace,
onSelect,
initialSelectedIds,
disabledVmIds,
showActions,
}: ProviderVirtualMachinesListProps) => {
return (
Expand All @@ -28,6 +30,7 @@ export const MemoizedProviderVirtualMachinesList = memo(
namespace={namespace}
onSelect={onSelect}
initialSelectedIds={initialSelectedIds}
disabledVmIds={disabledVmIds}
showActions={showActions}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const SelectSourceProvider: React.FC<{
dispatch: React.Dispatch<PageAction<CreateVmMigration, unknown>>;
filterDispatch: React.Dispatch<PlanCreatePageActionTypes>;
hideProviderSection?: boolean;
disabledVmIds?: string[];
}> = ({
filterState,
providers,
Expand All @@ -29,6 +30,7 @@ export const SelectSourceProvider: React.FC<{
dispatch,
filterDispatch,
hideProviderSection,
disabledVmIds,
}) => {
const { t } = useForkliftTranslation();

Expand Down Expand Up @@ -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}
/>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<MappingListProps> = ({
Expand All @@ -45,9 +52,9 @@ export const MappingList: FC<MappingListProps> = ({
noSourcesLabel,
isDisabled = false,
isEditable = true,
canEditItem,
}) => {
const { t } = useForkliftTranslation();

return (
<>
<DataList isCompact aria-label="">
Expand All @@ -64,6 +71,7 @@ export const MappingList: FC<MappingListProps> = ({
generalSourcesLabel={generalSourcesLabel}
noSourcesLabel={noSourcesLabel}
isEditable={isEditable}
canEditItem={(source) => canEditItem?.(source)}
/>
))}
</DataList>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
DataListItem,
DataListItemCells,
DataListItemRow,
Tooltip,
} from '@patternfly/react-core';
import {
Select,
Expand All @@ -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<MappingListItemProps> = ({
Expand All @@ -48,11 +50,14 @@ export const MappingListItem: FC<MappingListItemProps> = ({
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 });
};
Expand Down Expand Up @@ -93,7 +98,7 @@ export const MappingListItem: FC<MappingListItemProps> = ({
onSelect={onSelectSource}
selections={source}
isOpen={isSrcOpen}
isDisabled={!isEditable}
isDisabled={!isEditable || !canEditMapping}
aria-labelledby=""
isGrouped
menuAppendTo={() => document.body}
Expand All @@ -112,7 +117,7 @@ export const MappingListItem: FC<MappingListItemProps> = ({
onSelect={onSelectDestination}
selections={destination}
isOpen={isTrgOpen}
isDisabled={!isEditable}
isDisabled={!isEditable || !canEditMapping}
aria-labelledby=""
menuAppendTo={() => document.body}
>
Expand All @@ -129,13 +134,30 @@ export const MappingListItem: FC<MappingListItemProps> = ({
aria-label={t('Actions')}
aria-labelledby=""
>
<Button
onClick={onClick}
variant="plain"
aria-label={t('Delete mapping')}
key="delete-action"
icon={<MinusCircleIcon />}
/>
{!canEditMapping ? (
<Tooltip
content={t(
`This mapping's associated VM has been migrated and can no longer be modified.`,
)}
>
<Button
onClick={onClick}
variant="plain"
aria-label={t('Delete mapping')}
key="delete-action"
icon={<MinusCircleIcon />}
isAriaDisabled
/>
</Tooltip>
) : (
<Button
onClick={onClick}
variant="plain"
aria-label={t('Delete mapping')}
key="delete-action"
icon={<MinusCircleIcon />}
/>
)}
</DataListAction>
) : null}
</DataListItemRow>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import React, { useEffect, useReducer } from 'react';
import { SectionHeading } from 'src/components/headers/SectionHeading';
import { useOpenShiftNetworks, useSourceNetworks } from 'src/modules/Providers/hooks/useNetworks';
import { useOpenShiftStorages, useSourceStorages } from 'src/modules/Providers/hooks/useStorages';
import { useInventoryVms } from 'src/modules/Providers/views/details/tabs/VirtualMachines/utils/hooks/useInventoryVms';
import { getNetworksUsedBySelectedVms } from 'src/modules/Providers/views/migrate/reducer/getNetworksUsedBySelectedVMs';
import { getStoragesUsedBySelectedVms } from 'src/modules/Providers/views/migrate/reducer/getStoragesUsedBySelectedVMs';
import { useForkliftTranslation } from 'src/utils/i18n';

import {
Expand All @@ -17,6 +20,8 @@ import {
import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';
import { Alert, PageSection } from '@patternfly/react-core';

import { getVMMigrationStatus } from '../VirtualMachines/Migration/MigrationVirtualMachinesList';

import {
PlanMappingsSection,
planMappingsSectionReducer,
Expand Down Expand Up @@ -178,6 +183,24 @@ export const PlanMappingsInitSection: React.FC<PlanMappingsInitSectionProps> = (
const [targetStorages, targetStoragesLoading, targetStoragesError] =
useOpenShiftStorages(targetProvider);

const [vmData] = useInventoryVms(
{ provider: sourceProvider },
providersLoaded,
providersLoadError,
);
const migratedVmIds = plan?.status?.migration?.vms?.reduce((migrated, vm) => {
if (getVMMigrationStatus(vm) === 'Succeeded') {
migrated.push(vm.id);
}
return migrated;
}, []);
const migratedVms = vmData.filter((vm) => migratedVmIds?.includes(vm.vm.id));
const networkIdsUsedByMigratedVms =
sourceProvider?.spec?.type !== 'ovirt' ? getNetworksUsedBySelectedVms(migratedVms, []) : [];
const storageIdsUsedByMigratedVms = ['ovirt', 'openstack'].includes(sourceProvider?.spec?.type)
? []
: getStoragesUsedBySelectedVms({}, migratedVms, []);

if (
!providersLoaded ||
sourceNetworksLoading ||
Expand Down Expand Up @@ -240,6 +263,8 @@ export const PlanMappingsInitSection: React.FC<PlanMappingsInitSectionProps> = (
targetStorages={targetStorages}
planMappingsState={planMappingsState}
planMappingsDispatch={planMappingsDispatch}
networkIdsUsedByMigratedVms={networkIdsUsedByMigratedVms}
storageIdsUsedByMigratedVms={storageIdsUsedByMigratedVms}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import { Mapping, MappingList } from '../../components';
import {
canDeleteAndPatchPlanHooks,
hasPlanMappingsChanged,
hasSomeCompleteRunningVMs,
mapSourceNetworksIdsToLabels,
mapSourceStoragesIdsToLabels,
mapTargetNetworksIdsToLabels,
Expand Down Expand Up @@ -81,6 +80,8 @@ export type PlanMappingsSectionProps = {
type: string;
payload?;
}>;
networkIdsUsedByMigratedVms?: string[];
storageIdsUsedByMigratedVms?: string[];
};

export function planMappingsSectionReducer(
Expand Down Expand Up @@ -166,6 +167,8 @@ export const PlanMappingsSection: React.FC<PlanMappingsSectionProps> = ({
targetStorages,
planMappingsState: state,
planMappingsDispatch: dispatch,
networkIdsUsedByMigratedVms,
storageIdsUsedByMigratedVms,
}) => {
const { t } = useForkliftTranslation();

Expand Down Expand Up @@ -502,6 +505,24 @@ export const PlanMappingsSection: React.FC<PlanMappingsSectionProps> = ({
),
];

const canEditNetworkMapping = (source) => {
if (!networkIdsUsedByMigratedVms) {
return true;
}
const idLabelObj = mapSourceNetworksIdsToLabels(sourceNetworks);
const id = Object.keys(idLabelObj).find((key) => idLabelObj[key] === source);
return !networkIdsUsedByMigratedVms.includes(id);
};

const canEditStorageMapping = (source) => {
if (!storageIdsUsedByMigratedVms) {
return true;
}
const idLabelObj = mapSourceStoragesIdsToLabels(sourceStorages);
const id = Object.keys(idLabelObj).find((key) => idLabelObj[key] === source);
return !storageIdsUsedByMigratedVms.includes(id);
};

const PlanMappingsSectionEditMode: React.FC = () => {
const { t } = useForkliftTranslation();
return (
Expand Down Expand Up @@ -538,6 +559,7 @@ export const PlanMappingsSection: React.FC<PlanMappingsSectionProps> = ({
generalSourcesLabel={t('Other networks present on the source provider ')}
noSourcesLabel={t('No networks in this category')}
isDisabled={!isAddNetworkMapAvailable}
canEditItem={canEditNetworkMapping}
/>
</DescriptionListDescription>
</DescriptionListGroup>
Expand Down Expand Up @@ -566,6 +588,7 @@ export const PlanMappingsSection: React.FC<PlanMappingsSectionProps> = ({
generalSourcesLabel={t('Other storages present on the source provider ')}
noSourcesLabel={t('No storages in this category')}
isDisabled={!isAddStorageMapAvailable}
canEditItem={canEditStorageMapping}
/>
</DescriptionListDescription>
</DescriptionListGroup>
Expand All @@ -577,7 +600,7 @@ export const PlanMappingsSection: React.FC<PlanMappingsSectionProps> = ({

const PlanMappingsSectionViewMode: React.FC = () => {
const { t } = useForkliftTranslation();
const DisableEditMappings = hasSomeCompleteRunningVMs(plan) || !isPlanEditable(plan);
const DisableEditMappings = !isPlanEditable(plan);

return (
<>
Expand All @@ -596,7 +619,7 @@ export const PlanMappingsSection: React.FC<PlanMappingsSectionProps> = ({
<HelperText className="forklift-section-plan-helper-text">
<HelperTextItem variant="indeterminate">
{t(
'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 when the plan status does not enable editing.',
)}
</HelperTextItem>
</HelperText>
Expand Down
Loading

0 comments on commit 0eafaf8

Please sign in to comment.