Skip to content

Commit 8003b1e

Browse files
committed
Wizard: Add editable disk partition tables
This adds plain disk partition table and an option to add a volume group. Volume groups are added as a card that includes a file system table of logical volumes.
1 parent 45f1bd4 commit 8003b1e

File tree

21 files changed

+945
-103
lines changed

21 files changed

+945
-103
lines changed

playwright/Customizations/Filesystem.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ test('Create a blueprint with Filesystem customization', async ({
6161
await newPageAutomatic.close();
6262

6363
await frame
64-
.getByRole('radio', { name: 'Manually configure partitions' })
64+
.getByRole('radio', { name: 'Basic filesystem partitioning' })
6565
.click();
6666
const [newPageManual] = await Promise.all([
6767
page.context().waitForEvent('page'),
@@ -96,7 +96,7 @@ test('Create a blueprint with Filesystem customization', async ({
9696
.fill('/usb');
9797
await frame
9898
.getByRole('gridcell', { name: '1', exact: true })
99-
.getByPlaceholder('File system')
99+
.getByPlaceholder('Define minimum size')
100100
.fill('1000');
101101
await frame.getByRole('button', { name: 'GiB' }).nth(1).click();
102102
await frame.getByRole('option', { name: 'KiB' }).click();
@@ -147,11 +147,11 @@ test('Create a blueprint with Filesystem customization', async ({
147147

148148
await frame
149149
.getByRole('gridcell', { name: '1000', exact: true })
150-
.getByPlaceholder('File system')
150+
.getByPlaceholder('Define minimum size')
151151
.click();
152152
await frame
153153
.getByRole('gridcell', { name: '1000', exact: true })
154-
.getByPlaceholder('File system')
154+
.getByPlaceholder('Define minimum size')
155155
.fill('1024');
156156

157157
await frame.getByRole('button', { name: '/tmp' }).click();
@@ -219,7 +219,7 @@ test('Create a blueprint with Filesystem customization', async ({
219219

220220
const size = frame
221221
.getByRole('gridcell', { name: '1', exact: true })
222-
.getByPlaceholder('File system');
222+
.getByPlaceholder('Define minimum size');
223223
await expect(size).toHaveValue('1');
224224

225225
const unitButton = frame.getByRole('button', { name: 'GiB' }).nth(1);

src/Components/CreateImageWizard/steps/FileSystem/components/AdvancedPartitioning.tsx

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,73 @@ import React from 'react';
22

33
import {
44
Button,
5-
CodeBlock,
65
Content,
76
ContentVariants,
7+
FormGroup,
8+
TextInput,
89
} from '@patternfly/react-core';
9-
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
10+
import { ExternalLinkAltIcon, PlusCircleIcon } from '@patternfly/react-icons';
11+
import { v4 as uuidv4 } from 'uuid';
12+
13+
import FileSystemTable from './FileSystemTable';
14+
import VolumeGroups from './VolumeGroups';
1015

1116
import { PARTITIONING_URL } from '../../../../../constants';
12-
import { useAppSelector } from '../../../../../store/hooks';
17+
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
1318
import {
19+
addDiskPartition,
20+
changeDiskMinsize,
1421
selectDiskMinsize,
1522
selectDiskPartitions,
16-
selectDiskType,
1723
} from '../../../../../store/wizardSlice';
1824

1925
const AdvancedPartitioning = () => {
26+
const dispatch = useAppDispatch();
2027
const minsize = useAppSelector(selectDiskMinsize);
21-
const type = useAppSelector(selectDiskType);
2228
const diskPartitions = useAppSelector(selectDiskPartitions);
2329

30+
const handleAddPartition = () => {
31+
const id = uuidv4();
32+
dispatch(
33+
addDiskPartition({
34+
id,
35+
fs_type: 'ext4',
36+
min_size: '1',
37+
unit: 'GiB',
38+
type: 'plain',
39+
mountpoint: '/home',
40+
}),
41+
);
42+
};
43+
44+
const handleAddVolumeGroup = () => {
45+
const vgId = uuidv4();
46+
const lvId = uuidv4();
47+
dispatch(
48+
addDiskPartition({
49+
id: vgId,
50+
name: '',
51+
min_size: '1',
52+
unit: 'GiB',
53+
type: 'lvm',
54+
logical_volumes: [
55+
{
56+
id: lvId,
57+
name: '',
58+
mountpoint: '/home',
59+
min_size: '1',
60+
unit: 'GiB',
61+
fs_type: 'ext4',
62+
},
63+
],
64+
}),
65+
);
66+
};
67+
68+
const handleDiskMinsizeChange = (e: React.FormEvent, value: string) => {
69+
dispatch(changeDiskMinsize(value));
70+
};
71+
2472
return (
2573
<>
2674
<Content>
@@ -45,11 +93,45 @@ const AdvancedPartitioning = () => {
4593
</Button>
4694
</Content>
4795
</Content>
48-
<CodeBlock readOnly>
49-
<pre>{`minsize: ${minsize}
50-
type: ${type}
51-
diskPartitions: ${JSON.stringify(diskPartitions, null, 2)}`}</pre>
52-
</CodeBlock>
96+
<FormGroup label='Minimum disk size'>
97+
<TextInput
98+
aria-label='Minimum disk size input'
99+
value={minsize}
100+
type='text'
101+
onChange={handleDiskMinsizeChange}
102+
placeholder='Define minimum disk size'
103+
className='pf-v6-u-w-25'
104+
/>
105+
</FormGroup>
106+
{diskPartitions.filter((p) => p.type === 'plain').length > 0 && (
107+
<FileSystemTable
108+
partitions={diskPartitions.filter((p) => p.type === 'plain')}
109+
mode='disk-plain'
110+
/>
111+
)}
112+
<Content>
113+
<Button
114+
className='pf-v6-u-text-align-left'
115+
variant='link'
116+
icon={<PlusCircleIcon />}
117+
onClick={handleAddPartition}
118+
>
119+
Add plain partition
120+
</Button>
121+
</Content>
122+
<VolumeGroups
123+
volumeGroups={diskPartitions.filter((p) => p.type === 'lvm')}
124+
/>
125+
<Content>
126+
<Button
127+
className='pf-v6-u-text-align-left'
128+
variant='link'
129+
icon={<PlusCircleIcon />}
130+
onClick={handleAddVolumeGroup}
131+
>
132+
Add LVM volume group
133+
</Button>
134+
</Content>
53135
</>
54136
);
55137
};
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React from 'react';
2+
3+
import { Button } from '@patternfly/react-core';
4+
import { MinusCircleIcon } from '@patternfly/react-icons';
5+
import { Td, Tr } from '@patternfly/react-table';
6+
7+
import MinimumSize from './MinimumSize';
8+
import MountpointPrefix from './MountpointPrefix';
9+
import MountpointSuffix from './MountpointSuffix';
10+
import PartitionName from './PartitionName';
11+
import PartitionType from './PartitionType';
12+
import SizeUnit from './SizeUnit';
13+
14+
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
15+
import {
16+
removeDiskPartition,
17+
selectDiskPartitions,
18+
} from '../../../../../store/wizardSlice';
19+
import { FilesystemPartition, FscDiskPartition } from '../fscTypes';
20+
21+
export const FileSystemContext = React.createContext<boolean>(true);
22+
23+
type DiskRowPropTypes = {
24+
partition: FscDiskPartition;
25+
onDrop?: (event: React.DragEvent<HTMLTableRowElement>) => void;
26+
onDragEnd?: (event: React.DragEvent<HTMLTableRowElement>) => void;
27+
onDragStart?: (event: React.DragEvent<HTMLTableRowElement>) => void;
28+
};
29+
30+
const DiskRow = ({
31+
partition,
32+
onDragEnd,
33+
onDragStart,
34+
onDrop,
35+
}: DiskRowPropTypes) => {
36+
const dispatch = useAppDispatch();
37+
const partitions = useAppSelector(selectDiskPartitions);
38+
39+
const customization = 'disk';
40+
41+
const handleRemovePartition = (id: string) => {
42+
dispatch(removeDiskPartition(id));
43+
};
44+
45+
if (partition.type === 'lvm' || partition.type === 'btrfs') {
46+
return;
47+
}
48+
49+
return (
50+
<Tr
51+
draggable
52+
id={partition.id}
53+
onDrop={onDrop}
54+
onDragStart={onDragStart}
55+
onDragEnd={onDragEnd}
56+
>
57+
<Td
58+
draggableRow={{
59+
id: `draggable-row-${partition.id}`,
60+
}}
61+
/>
62+
{partition.type !== 'plain' && (
63+
<Td className='pf-m-width-20'>
64+
<PartitionName partition={partition} customization={customization} />
65+
</Td>
66+
)}
67+
<Td width={20}>
68+
<MountpointPrefix
69+
partition={partition as FilesystemPartition}
70+
customization={customization}
71+
/>
72+
</Td>
73+
{partition.mountpoint !== '/' &&
74+
!partition.mountpoint?.startsWith('/boot') &&
75+
!partition.mountpoint?.startsWith('/usr') ? (
76+
<Td width={20}>
77+
<MountpointSuffix
78+
partition={partition as FilesystemPartition}
79+
customization={customization}
80+
/>
81+
</Td>
82+
) : (
83+
<Td width={20} />
84+
)}
85+
<Td width={20}>
86+
<PartitionType partition={partition} customization={customization} />
87+
</Td>
88+
<Td width={20}>
89+
<MinimumSize partition={partition} customization={customization} />
90+
</Td>
91+
<Td width={10}>
92+
<SizeUnit partition={partition} customization={customization} />
93+
</Td>
94+
<Td width={10}>
95+
<Button
96+
variant='link'
97+
icon={<MinusCircleIcon />}
98+
onClick={() => handleRemovePartition(partition.id)}
99+
isDisabled={
100+
partition.mountpoint === '/' &&
101+
partitions.filter((p) => p.type === 'plain').length > 1
102+
}
103+
/>
104+
</Td>
105+
</Tr>
106+
);
107+
};
108+
109+
export default DiskRow;

src/Components/CreateImageWizard/steps/FileSystem/components/FileSystemConfiguration.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ const FileSystemConfiguration = () => {
9494
)} images`}
9595
/>
9696
)}
97-
<FileSystemTable partitions={filesystemPartitions} />
97+
<FileSystemTable partitions={filesystemPartitions} mode='filesystem' />
9898
<Content>
9999
<Button
100100
className='pf-v6-u-text-align-left'

src/Components/CreateImageWizard/steps/FileSystem/components/FileSystemPartition.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
changeFscMode,
99
changePartitioningMode,
1010
selectComplianceProfileID,
11-
selectDiskPartitions,
1211
selectFscMode,
1312
selectPartitioningMode,
1413
} from '../../../../../store/wizardSlice';
@@ -49,7 +48,7 @@ const FileSystemPartition = () => {
4948
<Radio
5049
id='basic-partitioning-radio'
5150
label={
52-
isAdvancedPartitioningEnabled
51+
process.env.IS_ON_PREMISE || isAdvancedPartitioningEnabled
5352
? 'Basic filesystem partitioning'
5453
: 'Manually configure partitions'
5554
}
@@ -60,7 +59,7 @@ const FileSystemPartition = () => {
6059
dispatch(changeFscMode('basic'));
6160
}}
6261
/>
63-
{isAdvancedPartitioningEnabled && (
62+
{(process.env.IS_ON_PREMISE || isAdvancedPartitioningEnabled) && (
6463
<Radio
6564
id='advanced-partitioning-radio'
6665
label='Advanced disk partitioning'

0 commit comments

Comments
 (0)