Skip to content

Commit

Permalink
Merge pull request #899 from rszwajko/migrateBtnV15
Browse files Browse the repository at this point in the history
🐾 Add a fast Create Plan page - step 7
  • Loading branch information
yaacov authored Feb 13, 2024
2 parents d3c6789 + 3b70f66 commit e4568b8
Show file tree
Hide file tree
Showing 17 changed files with 675 additions and 354 deletions.
1 change: 1 addition & 0 deletions packages/eslint-plugin/cspell.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fullname
cncf
omitempty
nics
NICS
pnic
virtio
SSHA
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"Add source and target providers for the migration.": "Add source and target providers for the migration.",
"All discovered networks have been mapped to the default network.": "All discovered networks have been mapped to the default network.",
"All discovered storages have been mapped to the default storage.": "All discovered storages have been mapped to the default storage.",
"All networks detected on the selected VMs require a mapping.": "All networks detected on the selected VMs require a mapping.",
"All storages detected on the selected VMs require a mapping.": "All storages detected on the selected VMs require a mapping.",
"API Error": "API Error",
"Application credential ID": "Application credential ID",
"Application credential name": "Application credential name",
Expand Down Expand Up @@ -64,9 +66,8 @@
"Controls the interval at which a new snapshot is requested prior to initiating a warm migration. The default value is 60 minutes.": "Controls the interval at which a new snapshot is requested prior to initiating a warm migration. The default value is 60 minutes.",
"Copied": "Copied",
"Copy": "Copy",
"Create": "Create",
"Create a migration plan and select VMs from the source provider for migration.": "Create a migration plan and select VMs from the source provider for migration.",
"Create and edit": "Create and edit",
"Create and start": "Create and start",
"Create NetworkMap": "Create NetworkMap",
"Create new provider": "Create new provider",
"Create plan": "Create plan",
Expand Down Expand Up @@ -160,6 +161,7 @@
"If true, the provider's CA certificate won't be validated.": "If true, the provider's CA certificate won't be validated.",
"If true, the provider's TLS certificate won't be validated.": "If true, the provider's TLS certificate won't be validated.",
"Image": "Image",
"Incomplete mapping": "Incomplete mapping",
"Information concerns": "Information concerns",
"Invalid password": "Invalid password",
"Invalid username": "Invalid username",
Expand Down Expand Up @@ -194,6 +196,8 @@
"Migrations (last 7 days)": "Migrations (last 7 days)",
"Migrations for virtualization": "Migrations for virtualization",
"MTU": "MTU",
"Multiple NICs mapped to Pod Networking ": "Multiple NICs mapped to Pod Networking ",
"Multiple NICs on the same network": "Multiple NICs on the same network",
"Must gather cleanup after (hours)": "Must gather cleanup after (hours)",
"Name": "Name",
"Name is primarily intended for creation idempotence and configuration definition. Cannot be updated.": "Name is primarily intended for creation idempotence and configuration definition. Cannot be updated.",
Expand All @@ -212,6 +216,8 @@
"Networks": "Networks",
"Networks used by the selected VMs": "Networks used by the selected VMs",
"New name was generated for the Network Map due to naming conflict.": "New name was generated for the Network Map due to naming conflict.",
"New name was generated for the Storage Map due to naming conflict.": "New name was generated for the Storage Map due to naming conflict.",
"NICs with empty NIC profile": "NICs with empty NIC profile",
"No credentials found.": "No credentials found.",
"No inventory data available.": "No inventory data available.",
"No NetworkMaps found.": "No NetworkMaps found.",
Expand All @@ -227,7 +233,6 @@
"No secret.": "No secret.",
"No StorageMaps found.": "No StorageMaps found.",
"No storages in this category": "No storages in this category",
"No target provider exists ": "No target provider exists ",
"Not Ready": "Not Ready",
"NUMA": "NUMA",
"Number of cluster in provider": "Number of cluster in provider",
Expand Down Expand Up @@ -344,6 +349,7 @@
"Storage": "Storage",
"Storage classes": "Storage classes",
"Storage domains": "Storage domains",
"Storage Map name re-generated": "Storage Map name re-generated",
"Storage map:": "Storage map:",
"Storage mappings have been re-generated": "Storage mappings have been re-generated",
"StorageMaps": "StorageMaps",
Expand Down Expand Up @@ -413,6 +419,9 @@
"Virtual Machine Migrations (last 7 days)": "Virtual Machine Migrations (last 7 days)",
"Virtual machines": "Virtual machines",
"Virtual Machines": "Virtual Machines",
"VM(s) with more than one interface mapped to Pod Networking were detected.": "VM(s) with more than one interface mapped to Pod Networking were detected.",
"VM(s) with multiple NICs on the same network were detected.": "VM(s) with multiple NICs on the same network were detected.",
"VM(s) with NICs that are not linked with a NIC profile were detected.": "VM(s) with NICs that are not linked with a NIC profile were detected.",
"VMs": "VMs",
"VMware only: vSphere product name.": "VMware only: vSphere product name.",
"VMware Virtual Disk Development Kit (VDDK) image, for example: quay.io/kubev2v/vddk:latest .": "VMware Virtual Disk Development Kit (VDDK) image, for example: quay.io/kubev2v/vddk:latest .",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,16 @@ import SectionHeading from 'src/components/headers/SectionHeading';
import { PlanCreateProgress } from 'src/modules/Plans/views/create';
import { ForkliftTrans, useForkliftTranslation } from 'src/utils/i18n';

import { Alert, AlertVariant, Button, Flex, FlexItem, PageSection } from '@patternfly/react-core';
import { LoadingDots } from '@kubev2v/common';
import { Alert, Button, Flex, FlexItem, PageSection } from '@patternfly/react-core';
import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';

import { PlansCreateForm } from './components/PlansCreateForm';
import { StateAlerts } from './components/StateAlerts';
import { startCreate } from './reducer/actions';
import { GeneralAlerts } from './types';
import { isDone } from './reducer/helpers';
import { useFetchEffects } from './useFetchEffects';
import { useSaveEffect } from './useSaveEffect';

const generalMessages = (
t: (key: string) => string,
): { [key in GeneralAlerts]: { title: string; body: string } } => ({
NEXT_VALID_PROVIDER_SELECTED: { title: t('Error'), body: t('No target provider exists ') },
});

const ProvidersCreateVmMigrationPage: FC = () => {
const { t } = useForkliftTranslation();
const history = useHistory();
Expand All @@ -35,25 +29,29 @@ const ProvidersCreateVmMigrationPage: FC = () => {
return <></>;
}

if (!isDone(state.flow.initialLoading) && !state.flow.apiError) {
return <LoadingDots />;
}

return (
<PageSection variant="light">
<Alert
className="co-alert forklift--create-vm-migration-plan--alert"
customIcon={<BellIcon />}
variant="info"
title={t('How to create a migration plan')}
>
<ForkliftTrans>
To migrate virtual machines select target provider, namespace, mappings and click the{' '}
<strong>Create</strong> button to crete the plan.
</ForkliftTrans>
</Alert>

<PlanCreateProgress step="migrate" />
<PlansCreateForm state={state} dispatch={dispatch}>
<Alert
className="co-alert forklift--create-vm-migration-plan--alert"
customIcon={<BellIcon />}
variant="info"
title={t('How to create a migration plan')}
>
<ForkliftTrans>
To migrate virtual machines select target provider, namespace, mappings and click the{' '}
<strong>Create</strong> button to crete the plan.
</ForkliftTrans>
</Alert>

<SectionHeading text={t('Migrate')} />
<PlanCreateProgress step="migrate" />

<PlansCreateForm state={state} dispatch={dispatch} />
<SectionHeading text={t('Migrate')} />
</PlansCreateForm>
{state.flow.apiError && (
<Alert
className="co-alert co-alert--margin-top"
Expand All @@ -64,29 +62,18 @@ const ProvidersCreateVmMigrationPage: FC = () => {
{state.flow.apiError.message || state.flow.apiError.toString()}
</Alert>
)}
<StateAlerts
variant={AlertVariant.danger}
messages={Array.from(state.alerts.general.errors).map((key) => ({
key,
...generalMessages(t)[key],
}))}
/>
<Flex>
<FlexItem>
<Button
variant="primary"
isDisabled={Object.values(state.validation).some(
(validation) => validation === 'error',
)}
isDisabled={
!!state.flow.apiError ||
Object.values(state.validation).some((validation) => validation === 'error')
}
isLoading={isLoading}
onClick={() => dispatch(startCreate())}
>
{t('Create and edit')}
</Button>
</FlexItem>
<FlexItem>
<Button variant="secondary" isDisabled={true} isLoading={isLoading}>
{t('Create and start')}
{t('Create')}
</Button>
</FlexItem>
<FlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export const EditableDescriptionItem: FC<{
content: ReactNode;
ariaEditLabel: string;
onEdit: () => void;
}> = ({ title, content, ariaEditLabel = 'Edit', onEdit }) => (
isDisabled?: boolean;
}> = ({ title, content, ariaEditLabel = 'Edit', onEdit, isDisabled = false }) => (
<DescriptionListGroup>
<DescriptionListTerm>{title}</DescriptionListTerm>
<DescriptionListDescription>
Expand All @@ -25,9 +26,9 @@ export const EditableDescriptionItem: FC<{
className="forklift-page-editable-description-item-button"
style={{ paddingTop: 0 }}
variant="plain"
icon={<PencilAltIcon />}
aria-label={ariaEditLabel}
onClick={onEdit}
{...(isDisabled
? {}
: { icon: <PencilAltIcon />, onClick: onEdit, 'aria-Label': ariaEditLabel })}
/>
</div>
</DescriptionListDescription>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import { StateAlerts } from './StateAlerts';

const buildNetworkMessages = (
t: (key: string) => string,
): { [key in NetworkAlerts]: { title: string; body: string } } => ({
): { [key in NetworkAlerts]: { title: string; body: string; blocker?: boolean } } => ({
NET_MAP_NAME_REGENERATED: {
title: t('Network Map name re-generated'),
body: t('New name was generated for the Network Map due to naming conflict.'),
Expand All @@ -63,17 +63,46 @@ const buildNetworkMessages = (
title: t('Network mappings have been re-generated'),
body: t('All discovered networks have been mapped to the default network.'),
},
MULTIPLE_NICS_ON_THE_SAME_NETWORK: {
title: t('Multiple NICs on the same network'),
body: t('VM(s) with multiple NICs on the same network were detected.'),
},
OVIRT_NICS_WITH_EMPTY_PROFILE: {
title: t('NICs with empty NIC profile'),
body: t('VM(s) with NICs that are not linked with a NIC profile were detected.'),
blocker: true,
},
UNMAPPED_NETWORKS: {
title: t('Incomplete mapping'),
body: t('All networks detected on the selected VMs require a mapping.'),
blocker: true,
},
MULTIPLE_NICS_MAPPED_TO_POD_NETWORKING: {
title: t('Multiple NICs mapped to Pod Networking '),
body: t('VM(s) with more than one interface mapped to Pod Networking were detected.'),
blocker: true,
},
});
const buildStorageMessages = (
t: (key: string) => string,
): { [key in StorageAlerts]: { title: string; body: string } } => ({
): { [key in StorageAlerts]: { title: string; body: string; blocker?: boolean } } => ({
STORAGE_MAPPING_REGENERATED: {
title: t('Storage mappings have been re-generated'),
body: t('All discovered storages have been mapped to the default storage.'),
},
STORAGE_MAP_NAME_REGENERATED: {
title: t('Storage Map name re-generated'),
body: t('New name was generated for the Storage Map due to naming conflict.'),
},
UNMAPPED_STORAGES: {
title: t('Incomplete mapping'),
body: t('All storages detected on the selected VMs require a mapping.'),
blocker: true,
},
});

export const PlansCreateForm = ({
children,
state: {
underConstruction: { plan, netMap, storageMap },
validation,
Expand All @@ -99,6 +128,7 @@ export const PlansCreateForm = ({
},
dispatch,
}: {
children?;
state: CreateVmMigrationPageState;
dispatch: (action: PageAction<unknown, unknown>) => void;
}) => {
Expand Down Expand Up @@ -137,6 +167,7 @@ export const PlansCreateForm = ({
}
>
<DrawerContentBody>
{children}
<DescriptionList
className="forklift-create-provider-edit-section"
columnModifier={{
Expand All @@ -160,6 +191,7 @@ export const PlansCreateForm = ({
id="planName"
value={plan.metadata.name}
validated={validation.planName}
isDisabled={flow.editingDone}
onChange={(value) => dispatch(setPlanName(value?.trim() ?? ''))}
/>
</FormGroup>
Expand All @@ -170,6 +202,7 @@ export const PlansCreateForm = ({
content={plan.metadata.name}
ariaEditLabel={t('Edit plan name')}
onEdit={() => setIsNameEdited(true)}
isDisabled={flow.editingDone}
/>
)}
<DetailsItem
Expand Down Expand Up @@ -211,6 +244,7 @@ export const PlansCreateForm = ({
onChange={(value) => dispatch(setPlanTargetProvider(value))}
validated={validation.targetProvider}
id="targetProvider"
isDisabled={flow.editingDone}
>
{[
<FormSelectOption
Expand Down Expand Up @@ -247,6 +281,7 @@ export const PlansCreateForm = ({
}
ariaEditLabel={t('Edit target provider')}
onEdit={() => setIsTargetProviderEdited(true)}
isDisabled={flow.editingDone}
/>
)}
{isTargetNamespaceEdited ||
Expand All @@ -264,6 +299,7 @@ export const PlansCreateForm = ({
onChange={(value) => dispatch(setPlanTargetNamespace(value))}
validated={validation.targetNamespace}
id="targetNamespace"
isDisabled={flow.editingDone}
>
{[
<FormSelectOption
Expand Down Expand Up @@ -302,6 +338,7 @@ export const PlansCreateForm = ({
}
ariaEditLabel={t('Edit target namespace')}
onEdit={() => setIsTargetNamespaceEdited(true)}
isDisabled={flow.editingDone}
/>
)}
<DescriptionListGroup>
Expand All @@ -318,6 +355,14 @@ export const PlansCreateForm = ({
</span>
</DescriptionListTerm>
<DescriptionListDescription className="forklift-page-mapping-list">
<StateAlerts
variant={AlertVariant.danger}
messages={Array.from(alerts.networkMappings.errors).map((key) => ({
key,
...networkMessages[key],
}))}
onClose={(key) => dispatch(removeAlert(key as NetworkAlerts))}
/>
<StateAlerts
variant={AlertVariant.warning}
messages={Array.from(alerts.networkMappings.warnings).map((key) => ({
Expand Down Expand Up @@ -356,6 +401,14 @@ export const PlansCreateForm = ({
</span>
</DescriptionListTerm>
<DescriptionListDescription className="forklift-page-mapping-list">
<StateAlerts
variant={AlertVariant.danger}
messages={Array.from(alerts.storageMappings.errors).map((key) => ({
key,
...storageMessages[key],
}))}
onClose={(key) => dispatch(removeAlert(key as StorageAlerts))}
/>
<StateAlerts
variant={AlertVariant.warning}
messages={Array.from(alerts.storageMappings.warnings).map((key) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ import { Alert, AlertActionCloseButton, AlertVariant } from '@patternfly/react-c

export const StateAlerts: FC<{
variant: AlertVariant;
messages: { key: string; title: string; body: string }[];
messages: { key: string; title: string; body: string; blocker?: boolean }[];
onClose?: (key: string) => void;
}> = ({ variant, messages, onClose }) => (
<>
{messages.map(({ key, title, body }) => (
{messages.map(({ key, title, body, blocker }) => (
<Alert
key={key}
isInline
actionClose={onClose ? <AlertActionCloseButton onClose={() => onClose(key)} /> : undefined}
actionClose={
onClose && !blocker ? <AlertActionCloseButton onClose={() => onClose(key)} /> : undefined
}
variant={variant}
title={title}
>
Expand Down
Loading

0 comments on commit e4568b8

Please sign in to comment.