-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a plan details item to edit boot disk
Signed-off-by: yaacov <[email protected]>
- Loading branch information
Showing
9 changed files
with
322 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
...modules/Plans/views/details/components/SettingsSection/components/RootDiskDetailsItem.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
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 } from '@patternfly/react-core'; | ||
|
||
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 root disk is the default option | ||
const color = !diskKey ? 'green' : 'grey'; | ||
|
||
return ( | ||
<Label isCompact color={color}> | ||
{diskLabel} | ||
</Label> | ||
); | ||
}; |
1 change: 1 addition & 0 deletions
1
...ole-plugin/src/modules/Plans/views/details/components/SettingsSection/components/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
...dules/Plans/views/details/components/SettingsSection/modals/EditRootDisk/EditRootDisk.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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[]; | ||
} | ||
>; |
15 changes: 15 additions & 0 deletions
15
...s/views/details/components/SettingsSection/modals/EditRootDisk/editRootDiskModalAlert.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
</> | ||
} | ||
/> | ||
); |
30 changes: 30 additions & 0 deletions
30
...ns/views/details/components/SettingsSection/modals/EditRootDisk/editRootDiskModalBody.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
</> | ||
); |
84 changes: 84 additions & 0 deletions
84
...ans/views/details/components/SettingsSection/modals/EditRootDisk/getRootDiskLabelByKey.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/** | ||
* Type definition for DiskOption. | ||
* @typedef {Object} DiskOption | ||
* @property {string} key - The key representing the disk option. | ||
* @property {string} description - The description of the disk option. | ||
*/ | ||
export type DiskOption = { | ||
key: string; | ||
description: string; | ||
}; | ||
|
||
/** | ||
* Generates an array of disk options. | ||
* @param {Function} [t=(text: string) => text] - Translation function. | ||
* @returns {DiskOption[]} Array of disk options. | ||
*/ | ||
export const diskOptions = (t = (text: string) => text): DiskOption[] => [ | ||
{ key: '', description: t('Boot from first root device') }, | ||
{ key: '/dev/sda', description: t('Boot from the first hard drive') }, | ||
{ | ||
key: '/dev/sda1', | ||
description: t('Boot from the first partition on the first hard drive'), | ||
}, | ||
{ | ||
key: '/dev/sda2', | ||
description: t('Boot from the second partition on the first hard drive'), | ||
}, | ||
{ key: '/dev/sdb', description: t('Boot from the second hard drive') }, | ||
{ | ||
key: '/dev/sdb1', | ||
description: t('Boot from the first partition on the second hard drive'), | ||
}, | ||
{ | ||
key: '/dev/sdb2', | ||
description: t('Boot from the second partition on the second hard drive'), | ||
}, | ||
]; | ||
|
||
/** | ||
* Gets the label for a root disk by its key. | ||
* @param {string | number} key_ - The key representing the disk option. | ||
* @returns {string} The label for the root disk. | ||
*/ | ||
export const getRootDiskLabelByKey = (key_: string | number): string => { | ||
const diskLetters = 'abcdefghijklmnopqrstuvwxyz'; | ||
const partitionNumbers = '0123456789'; | ||
|
||
// Default is first root disk | ||
if (!key_) { | ||
return 'First root device'; | ||
} | ||
|
||
const key = key_.toString(); | ||
|
||
if (key.startsWith('/dev/sd') && key.length >= 8) { | ||
const diskLetter = key[7]; | ||
const partitionNumber = key.length > 8 ? key.slice(8) : ''; | ||
|
||
const diskIndex = diskLetters.indexOf(diskLetter); | ||
if (diskIndex === -1 || (partitionNumber && !partitionNumbers.includes(partitionNumber[0]))) { | ||
// If format is unrecognized, just return the key as label | ||
return key; | ||
} | ||
|
||
const diskPosition = [ | ||
'First', | ||
'Second', | ||
'Third', | ||
'Fourth', | ||
'Fifth', | ||
'Sixth', | ||
'Seventh', | ||
'Eighth', | ||
'Ninth', | ||
'Tenth', | ||
][diskIndex]; | ||
const partitionPosition = partitionNumber ? `${partitionNumber} partition` : ''; | ||
|
||
return `${diskPosition} HD${partitionPosition ? ` ${partitionPosition}` : ''} (${key})`; | ||
} else { | ||
// If format is unrecognized, just return the key as label | ||
return key; | ||
} | ||
}; |
6 changes: 6 additions & 0 deletions
6
...n/src/modules/Plans/views/details/components/SettingsSection/modals/EditRootDisk/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// @index(['./*', /style/g], f => `export * from '${f.path}';`) | ||
export * from './EditRootDisk'; | ||
export * from './editRootDiskModalAlert'; | ||
export * from './editRootDiskModalBody'; | ||
export * from './getRootDiskLabelByKey'; | ||
// @endindex |