Skip to content

Commit

Permalink
Merge pull request #1205 from yaacov/add-rootDisk
Browse files Browse the repository at this point in the history
🐾 Add a plan details item to edit boot disk
  • Loading branch information
yaacov authored Jun 18, 2024
2 parents 8482a13 + 02db59b commit 0af2f6e
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
"Assessment": "Assessment",
"Authentication type": "Authentication type",
"Bandwidth": "Bandwidth",
"Boot from first root device": "Boot from first root device",
"Boot from the first hard drive": "Boot from the first hard drive",
"Boot from the first partition on the first hard drive": "Boot from the first partition on the first hard drive",
"Boot from the first partition on the second hard drive": "Boot from the first partition on the second hard drive",
"Boot from the second hard drive": "Boot from the second hard drive",
"Boot from the second partition on the first hard drive": "Boot from the second partition on the first hard drive",
"Boot from the second partition on the second hard drive": "Boot from the second partition on the second hard drive",
"CA certificate": "CA certificate",
"CA certificate - disabled when 'Skip certificate validation' is selected": "CA certificate - disabled when 'Skip certificate validation' is selected",
"CA certificate - leave empty to use system CA certificates": "CA certificate - leave empty to use system CA certificates",
Expand All @@ -60,6 +67,7 @@
"Cannot retrieve certificate": "Cannot retrieve certificate",
"Category": "Category",
"Certificate change detected": "Certificate change detected",
"Choose the root filesystem to be converted.": "Choose the root filesystem to be converted.",
"Clear all filters": "Clear all filters",
"Click the pencil for setting provider web UI link": "Click the pencil for setting provider web UI link",
"Click the update credentials button to save your changes, button is disabled until a change is detected.": "Click the update credentials button to save your changes, button is disabled until a change is detected.",
Expand Down Expand Up @@ -143,6 +151,7 @@
"Edit Provider Credentials": "Edit Provider Credentials",
"Edit provider credentials.\n Use this link to edit the providers credentials instead of editing the secret directly.": "Edit provider credentials.\n Use this link to edit the providers credentials instead of editing the secret directly.",
"Edit provider web UI link": "Edit provider web UI link",
"Edit root device": "Edit root device",
"Edit Snapshot polling interval (seconds)": "Edit Snapshot polling interval (seconds)",
"Edit StorageMap": "Edit StorageMap",
"Edit URL": "Edit URL",
Expand Down Expand Up @@ -178,6 +187,7 @@
"Filter by template": "Filter by template",
"Filter by tenant": "Filter by tenant",
"Filter provider": "Filter provider",
"First root device": "First root device",
"Flavor": "Flavor",
"Folder": "Folder",
"GPUs/Host Devices": "GPUs/Host Devices",
Expand Down Expand Up @@ -381,6 +391,7 @@
"Restore default columns": "Restore default columns",
"Return to the providers list page": "Return to the providers list page",
"Reveal values": "Reveal values",
"Root device": "Root device",
"Run the migration plan.": "Run the migration plan.",
"Running": "Running",
"Running virtual machines": "Running virtual machines",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,8 @@
.forklift-page-plan-resources-td-fractional {
text-align: left;
display: inline-block;
}
}

.forklift-page-plan-settings-icon {
padding-right: var(--pf-global--spacer--sm);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DescriptionList } from '@patternfly/react-core';
import {
PreserveClusterCpuModelDetailsItem,
PreserveStaticIPsDetailsItem,
RootDiskDetailsItem,
SetLUKSEncryptionPasswordsDetailsItem,
TargetNamespaceDetailsItem,
TransferNetworkDetailsItem,
Expand Down Expand Up @@ -75,6 +76,10 @@ export const SettingsSectionInternal: React.FC<SettingsSectionProps> = ({ obj, p
{['vsphere'].includes(sourceProvider?.spec?.type) && (
<SetLUKSEncryptionPasswordsDetailsItem resource={obj} canPatch={permissions.canPatch} />
)}

{['vsphere'].includes(sourceProvider?.spec?.type) && (
<RootDiskDetailsItem resource={obj} canPatch={permissions.canPatch} />
)}
</DescriptionList>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react';
import { useModal } from 'src/modules/Providers/modals';
import { DetailsItem } from 'src/modules/Providers/utils';
import { useForkliftTranslation } from 'src/utils/i18n';

import { Label, Tooltip } from '@patternfly/react-core';
import { ExclamationTriangleIcon } from '@patternfly/react-icons';

import { PlanDetailsItemProps } from '../../DetailsSection';
import { VIRT_V2V_HELP_LINK } from '../modals';
import { getRootDiskLabelByKey } from '../modals/EditRootDisk';
import { EditRootDisk } from '../modals/EditRootDisk/EditRootDisk';

export const RootDiskDetailsItem: React.FC<PlanDetailsItemProps> = ({
resource,
canPatch,
helpContent,
}) => {
const { t } = useForkliftTranslation();
const { showModal } = useModal();

const defaultHelpContent = t(`Choose the root filesystem to be converted.`);

const rootDisk = resource?.spec?.vms?.[0].rootDisk;

return (
<DetailsItem
title={t('Root device')}
content={getDiskLabel(rootDisk)}
helpContent={helpContent ?? defaultHelpContent}
moreInfoLink={VIRT_V2V_HELP_LINK}
crumbs={['spec', 'vms', 'rootDisk']}
onEdit={canPatch && (() => showModal(<EditRootDisk resource={resource} />))}
/>
);
};

/**
* Generates a label component for the given disk key.
* @param {string} diskKey - The key representing the disk option.
* @returns {JSX.Element} The label component for the disk.
*/
const getDiskLabel = (diskKey: string) => {
const diskLabel = getRootDiskLabelByKey(diskKey);

// First boot disk, color green
if (!diskKey) {
return (
<Label isCompact color={'green'}>
{diskLabel}
</Label>
);
}

// Known boot disk format, color grey.
if (diskKey.startsWith('/dev/sd')) {
return (
<Label isCompact color={'grey'}>
{diskLabel}
</Label>
);
}

// Unknown boot disk format.
return (
<Tooltip
content={
'Root filesystem format should start with "/dev/sd[X]", see documentation for more information.'
}
>
<Label isCompact color={'orange'}>
<span className="forklift-page-plan-settings-icon">
<ExclamationTriangleIcon color="orange" />
</span>
{diskLabel}
</Label>
</Tooltip>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @index(['./*', /style/g], f => `export * from '${f.path}';`)
export * from './PreserveClusterCpuModelDetailsItem';
export * from './PreserveStaticIPsDetailsItem';
export * from './RootDiskDetailsItem';
export * from './SetLUKSEncryptionPasswordsDetailsItem';
export * from './TargetNamespaceDetailsItem';
export * from './TransferNetworkDetailsItem';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React from 'react';
import { FilterableSelect } from 'src/components/FilterableSelect/FilterableSelect';
import {
EditModal,
EditModalProps,
ModalInputComponentType,
OnConfirmHookType,
} from 'src/modules/Providers/modals';
import { useForkliftTranslation } from 'src/utils/i18n';

import { Modify, PlanModel, V1beta1Plan } from '@kubev2v/types';
import { K8sModel, k8sPatch } from '@openshift-console/dynamic-plugin-sdk';
import { HelperText, HelperTextItem, Text } from '@patternfly/react-core';

import { editRootDiskModalAlert } from './editRootDiskModalAlert';
import { editRootDiskModalBody } from './editRootDiskModalBody';
import { diskOptions, getRootDiskLabelByKey } from './getRootDiskLabelByKey';

const onConfirm: OnConfirmHookType = async ({ resource, model, newValue }) => {
const plan = resource as V1beta1Plan;

const resourceValue = plan?.spec?.vms;
const op = resourceValue ? 'replace' : 'add';
const newVMs = resourceValue.map((vm) => ({
...vm,
rootDisk: newValue || undefined,
}));

const obj = await k8sPatch({
model: model,
resource: resource,
data: [
{
op,
path: '/spec/vms',
value: newVMs || undefined,
},
],
});

return obj;
};

interface DropdownRendererProps {
value: string | number;
onChange: (string) => void;
}

const RootDiskInputFactory: () => ModalInputComponentType = () => {
const DropdownRenderer: React.FC<DropdownRendererProps> = ({ value, onChange }) => {
const { t } = useForkliftTranslation();
const options = diskOptions(t);

const dropdownItems = options.map((option) => ({
itemId: option.key,
children: (
<>
<Text>{getRootDiskLabelByKey(option.key)}</Text>
{option.description && (
<HelperText>
<HelperTextItem variant="indeterminate">{option.description}</HelperTextItem>
</HelperText>
)}
</>
),
}));

return (
<FilterableSelect
selectOptions={dropdownItems}
value={value as string}
onSelect={onChange}
canCreate
placeholder={t('First root device')}
></FilterableSelect>
);
};

return DropdownRenderer;
};

export const EditRootDisk: React.FC<EditRootDiskProps> = (props) => {
const { t } = useForkliftTranslation();

const plan = props.resource;
const rootDisk = plan.spec.vms?.[0]?.rootDisk;
const allVMsHasMatchingRootDisk = plan.spec.vms.every((vm) => vm?.rootDisk === rootDisk);

return (
<EditModal
{...props}
jsonPath={(obj: V1beta1Plan) => obj?.spec?.vms?.[0]?.rootDisk}
title={props?.title || t('Edit root device')}
label={props?.label || t('Root device')}
model={PlanModel}
onConfirmHook={onConfirm}
body={
<>
{editRootDiskModalBody}
{!allVMsHasMatchingRootDisk && editRootDiskModalAlert}
</>
}
InputComponent={RootDiskInputFactory()}
/>
);
};

export type EditRootDiskProps = Modify<
EditModalProps,
{
resource: V1beta1Plan;
title?: string;
label?: string;
model?: K8sModel;
jsonPath?: string | string[];
}
>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { AlertMessageForModals } from 'src/modules/Providers/modals';

export const editRootDiskModalAlert = (
<AlertMessageForModals
variant="warning"
title={'The plan rootDisk keys was manually configured'}
message={
<>
<p>Warning: not all virtual machines are configures using the same root disk number,</p>
<p>updating the root disk number will override the current configuration.</p>
</>
}
/>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { ForkliftTrans } from 'src/utils';

import { ExternalLink } from '@kubev2v/common';

import { VIRT_V2V_HELP_LINK } from '../EditLUKSEncryptionPasswords';

export const editRootDiskModalBody = (
<>
<ForkliftTrans>
<p>Choose the root filesystem to be converted.</p>
<br />
<p>
Default behavior is to choose the first root device in the case of a multi-boot operating
system. Since this is a heuristic, it may sometimes choose the wrong one.
</p>
<br />
<p>
When using a multi-boot VM, you can also name a specific root device, eg.{' '}
<strong>/dev/sda2</strong> would mean to use the second partition on the first hard drive.
If the named root device does not exist or was not detected as a root device, the migration
will fail.{' '}
<ExternalLink isInline href={VIRT_V2V_HELP_LINK}>
Learn more
</ExternalLink>
.
</p>
</ForkliftTrans>
</>
);
Loading

0 comments on commit 0af2f6e

Please sign in to comment.